diff --git a/src/clevo_acpi.c b/src/clevo_acpi.c index 10a3f96..e1abd8f 100644 --- a/src/clevo_acpi.c +++ b/src/clevo_acpi.c @@ -32,7 +32,7 @@ struct clevo_acpi_driver_data_t { static struct clevo_acpi_driver_data_t *active_driver_data = NULL; -static u32 clevo_acpi_evaluate(struct acpi_device *device, u8 cmd, u32 arg, u32 *result) +static u32 clevo_acpi_evaluate(struct acpi_device *device, u8 cmd, u32 arg, union acpi_object **result) { u32 status; acpi_handle handle; @@ -69,23 +69,17 @@ static u32 clevo_acpi_evaluate(struct acpi_device *device, u8 cmd, u32 arg, u32 if (!out_obj) { pr_err("failed to evaluate _DSM\n"); status = -1; - } else { - if (out_obj->type == ACPI_TYPE_INTEGER) { - if (!IS_ERR_OR_NULL(result)) - *result = (u32) out_obj->integer.value; - // pr_debug("evaluate _DSM cmd: %0#4x arg: %0#10x\n", cmd, arg); - } else { - pr_err("unknown output from _DSM\n"); - status = -ENODATA; + } + else { + if (!IS_ERR_OR_NULL(result)) { + *result = out_obj; } } - ACPI_FREE(out_obj); - return status; } -u32 clevo_acpi_interface_method_call(u8 cmd, u32 arg, u32 *result_value) +u32 clevo_acpi_interface_method_call(u8 cmd, u32 arg, union acpi_object **result_value) { u32 status = 0; @@ -146,9 +140,19 @@ static void clevo_acpi_remove(struct acpi_device *device) void clevo_acpi_notify(struct acpi_device *device, u32 event) { u32 event_value; + union acpi_object *out_obj; + u32 status; // struct clevo_acpi_driver_data_t *clevo_acpi_driver_data; - clevo_acpi_evaluate(device, 0x01, 0, &event_value); + status = clevo_acpi_evaluate(device, 0x01, 0, &out_obj); + if (!status) { + if (out_obj->type == ACPI_TYPE_INTEGER) { + event_value = (u32)out_obj->integer.value; + } else { + pr_err("return type not integer, use clevo_evaluate_method2\n"); + } + ACPI_FREE(out_obj); + } pr_debug("clevo_acpi event: %0#6x, clevo event value: %0#6x\n", event, event_value); // clevo_acpi_driver_data = container_of(&device, struct clevo_acpi_driver_data_t, adev); @@ -199,7 +203,7 @@ module_acpi_driver(clevo_acpi_driver); MODULE_AUTHOR("TUXEDO Computers GmbH "); MODULE_DESCRIPTION("Driver for Clevo ACPI interface"); -MODULE_VERSION("0.0.3"); +MODULE_VERSION("0.1.0"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(acpi, clevo_acpi_device_ids); diff --git a/src/clevo_interfaces.h b/src/clevo_interfaces.h index e8f2bd5..fabeff5 100644 --- a/src/clevo_interfaces.h +++ b/src/clevo_interfaces.h @@ -20,6 +20,7 @@ #define CLEVO_INTERFACES_H #include +#include #define CLEVO_WMI_EVENT_GUID "ABBC0F6B-8EA1-11D1-00A0-C90629100000" #define CLEVO_WMI_EMAIL_GUID "ABBC0F6C-8EA1-11D1-00A0-C90629100000" @@ -37,6 +38,16 @@ #define CLEVO_CMD_GET_FLIGHTMODE_SW 0x07 #define CLEVO_CMD_GET_TOUCHPAD_SW 0x09 +#define CLEVO_CMD_GET_EVENT 0x01 + +#define CLEVO_CMD_GET_SPECS 0x0D // Returns buffer -> only works with clevo_evaluate_method2 +#define CLEVO_CMD_GET_BIOS_FEATURES_1 0x52 +#define CLEVO_CMD_GET_BIOS_FEATURES_1_SUB_WHITE_ONLY_KB 0x40000000 +#define CLEVO_CMD_GET_BIOS_FEATURES_1_SUB_3_ZONE_RGB_KB 0x00400000 + +#define CLEVO_CMD_GET_BIOS_FEATURES_2 0x7A +#define CLEVO_CMD_GET_BIOS_FEATURES_2_SUB_WHITE_ONLY_KB_MAX_5 0x4000 + // The clevo set commands expect a parameter #define CLEVO_CMD_SET_FANSPEED_VALUE 0x68 #define CLEVO_CMD_SET_FANSPEED_AUTO 0x69 @@ -45,18 +56,29 @@ #define CLEVO_CMD_SET_FLIGHTMODE_SW 0x20 #define CLEVO_CMD_SET_TOUCHPAD_SW 0x2a +#define CLEVO_CMD_SET_EVENTS_ENABLED 0x46 + +#define CLEVO_CMD_SET_KB_WHITE_LEDS 0x27 // Set brightness of single color keyboard backlights +#define CLEVO_CMD_SET_KB_RGB_LEDS 0x67 // Used to set color, brightness, blinking pattern, etc. +#define CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_0 0xF0000000 // 1-zone RGB and 3-zone RGB left +#define CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_1 0xF1000000 // 3-zone RGB center +#define CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_2 0xF2000000 // 3-Zone RGB right +#define CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_3 0xF3000000 // Unused on all known Clevo devices +#define CLEVO_CMD_SET_KB_LEDS_SUB_RGB_BRIGHTNESS 0xF4000000 + #define CLEVO_CMD_OPT 0x79 -#define CLEVO_OPT_SUBCMD_SET_PERF_PROF 0x19 +#define CLEVO_CMD_OPT_SUB_SET_PERF_PROF 0x19 struct clevo_interface_t { char *string_id; void (*event_callb)(u32); - u32 (*method_call)(u8, u32, u32*); + u32 (*method_call)(u8, u32, union acpi_object **); }; u32 clevo_keyboard_add_interface(struct clevo_interface_t *new_interface); u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface); u32 clevo_evaluate_method(u8 cmd, u32 arg, u32 *result); +u32 clevo_evaluate_method2(u8 cmd, u32 arg, union acpi_object **result); u32 clevo_get_active_interface_id(char **id_str); #define MODULE_ALIAS_CLEVO_WMI() \ diff --git a/src/clevo_keyboard.h b/src/clevo_keyboard.h index 278e937..817e50f 100644 --- a/src/clevo_keyboard.h +++ b/src/clevo_keyboard.h @@ -16,58 +16,370 @@ * You should have received a copy of the GNU General Public License * along with this software. If not, see . */ +#ifndef CLEVO_KEYBOARD_H +#define CLEVO_KEYBOARD_H + #include "tuxedo_keyboard_common.h" #include "clevo_interfaces.h" - -#define BRIGHTNESS_MIN 0 -#define BRIGHTNESS_MAX 255 -#define BRIGHTNESS_DEFAULT BRIGHTNESS_MAX - -#define REGION_LEFT 0xF0000000 -#define REGION_CENTER 0xF1000000 -#define REGION_RIGHT 0xF2000000 -#define REGION_EXTRA 0xF3000000 - -#define KEYBOARD_BRIGHTNESS 0xF4000000 - -#define KB_COLOR_DEFAULT 0xFFFFFF // White -#define DEFAULT_BLINKING_PATTERN 0 - -// Submethod IDs for the CLEVO_GET interface method -#define CLEVO_METHOD_ID_GET_EVENT 0x01 -#define CLEVO_METHOD_ID_GET_AP 0x46 -#define CLEVO_METHOD_ID_SET_KB_LEDS 0x67 /* used to set color, brightness, - blinking pattern, etc. */ - +#include "clevo_leds.h" // Clevo event codes -#define CLEVO_EVENT_DECREASE_BACKLIGHT 0x81 -#define CLEVO_EVENT_INCREASE_BACKLIGHT 0x82 -#define CLEVO_EVENT_NEXT_BLINKING_PATTERN 0x83 -#define CLEVO_EVENT_TOGGLE_STATE 0x9F +#define CLEVO_EVENT_KB_LEDS_DECREASE 0x81 +#define CLEVO_EVENT_KB_LEDS_INCREASE 0x82 +#define CLEVO_EVENT_KB_LEDS_CYCLE_MODE 0x83 +#define CLEVO_EVENT_KB_LEDS_CYCLE_BRIGHTNESS 0x8A +#define CLEVO_EVENT_KB_LEDS_TOGGLE 0x9F -#define CLEVO_EVENT_CYCLE_BRIGHTNESS 0x8A -#define CLEVO_EVENT_TOUCHPAD_TOGGLE 0x5D -#define CLEVO_EVENT_TOUCHPAD_OFF 0xFC -#define CLEVO_EVENT_TOUCHPAD_ON 0xFD +#define CLEVO_EVENT_TOUCHPAD_TOGGLE 0x5D +#define CLEVO_EVENT_TOUCHPAD_OFF 0xFC +#define CLEVO_EVENT_TOUCHPAD_ON 0xFD -#define CLEVO_EVENT_RFKILL1 0x85 -#define CLEVO_EVENT_RFKILL2 0x86 +#define CLEVO_EVENT_RFKILL1 0x85 +#define CLEVO_EVENT_RFKILL2 0x86 -struct tuxedo_keyboard_driver clevo_keyboard_driver; +#define CLEVO_KB_MODE_DEFAULT 0 // "CUSTOM"/Static Color static struct clevo_interfaces_t { struct clevo_interface_t *wmi; struct clevo_interface_t *acpi; } clevo_interfaces; -struct clevo_interface_t *active_clevo_interface; - -void clevo_keyboard_write_state(void); -void clevo_keyboard_event_callb(u32 event); +static struct clevo_interface_t *active_clevo_interface; static DEFINE_MUTEX(clevo_keyboard_interface_modification_lock); +static struct key_entry clevo_keymap[] = { + // Keyboard backlight (RGB versions) + { KE_KEY, CLEVO_EVENT_KB_LEDS_DECREASE, { KEY_KBDILLUMDOWN } }, + { KE_KEY, CLEVO_EVENT_KB_LEDS_INCREASE, { KEY_KBDILLUMUP } }, + { KE_KEY, CLEVO_EVENT_KB_LEDS_TOGGLE, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, CLEVO_EVENT_KB_LEDS_CYCLE_MODE, { KEY_LIGHTS_TOGGLE } }, + // Single cycle key (white only versions) + { KE_KEY, CLEVO_EVENT_KB_LEDS_CYCLE_BRIGHTNESS, { KEY_KBDILLUMTOGGLE } }, + + // Touchpad + // The weirdly named touchpad toggle key that is implemented as KEY_F21 "everywhere" + // (instead of KEY_TOUCHPAD_TOGGLE or on/off) + // Most "new" devices just provide one toggle event + { KE_KEY, CLEVO_EVENT_TOUCHPAD_TOGGLE, { KEY_F21 } }, + // Some "old" devices produces on/off events + { KE_KEY, CLEVO_EVENT_TOUCHPAD_OFF, { KEY_F21 } }, + { KE_KEY, CLEVO_EVENT_TOUCHPAD_ON, { KEY_F21 } }, + // The alternative key events (currently not used) + // { KE_KEY, CLEVO_EVENT_TOUCHPAD_OFF, { KEY_TOUCHPAD_OFF } }, + // { KE_KEY, CLEVO_EVENT_TOUCHPAD_ON, { KEY_TOUCHPAD_ON } }, + // { KE_KEY, CLEVO_EVENT_TOUCHPAD_TOGGLE, { KEY_TOUCHPAD_TOGGLE } }, + + // Rfkill still needed by some devices + { KE_KEY, CLEVO_EVENT_RFKILL1, { KEY_RFKILL } }, + { KE_IGNORE, CLEVO_EVENT_RFKILL2, { KEY_RFKILL } }, // Older rfkill event + // Note: Volume events need to be ignored as to not interfere with built-in functionality + { KE_IGNORE, 0xfa, { KEY_UNKNOWN } }, // Appears by volume up/down + { KE_IGNORE, 0xfb, { KEY_UNKNOWN } }, // Appears by mute toggle + + { KE_END, 0 } +}; + +// Keyboard struct +static struct kbd_led_state_t { + u8 has_mode; + u8 mode; + u8 whole_kbd_color; +} kbd_led_state = { + .has_mode = 1, + .mode = CLEVO_KB_MODE_DEFAULT, + .whole_kbd_color = 7, +}; + +static struct kbd_backlight_mode_t { + u8 key; + u32 value; + const char *const name; +} kbd_backlight_modes[] = { + { .key = 0, .value = 0x00000000, .name = "CUSTOM"}, + { .key = 1, .value = 0x1002a000, .name = "BREATHE"}, + { .key = 2, .value = 0x33010000, .name = "CYCLE"}, + { .key = 3, .value = 0x80000000, .name = "DANCE"}, + { .key = 4, .value = 0xA0000000, .name = "FLASH"}, + { .key = 5, .value = 0x70000000, .name = "RANDOM_COLOR"}, + { .key = 6, .value = 0x90000000, .name = "TEMPO"}, + { .key = 7, .value = 0xB0000000, .name = "WAVE"} +}; + +u32 clevo_evaluate_method2(u8 cmd, u32 arg, union acpi_object **result) +{ + if (IS_ERR_OR_NULL(active_clevo_interface)) { + pr_err("clevo_keyboard: no active interface while attempting cmd %02x arg %08x\n", cmd, arg); + return -ENODEV; + } + return active_clevo_interface->method_call(cmd, arg, result); +} +EXPORT_SYMBOL(clevo_evaluate_method2); + +u32 clevo_evaluate_method(u8 cmd, u32 arg, u32 *result) +{ + u32 status = 0; + union acpi_object *out_obj; + + status = clevo_evaluate_method2(cmd, arg, &out_obj); + if (status) { + return status; + } + else { + if (out_obj->type == ACPI_TYPE_INTEGER) { + if (!IS_ERR_OR_NULL(result)) + *result = (u32) out_obj->integer.value; + } else { + pr_err("return type not integer, use clevo_evaluate_method2\n"); + status = -ENODATA; + } + ACPI_FREE(out_obj); + } + + return status; +} +EXPORT_SYMBOL(clevo_evaluate_method); + +u32 clevo_get_active_interface_id(char **id_str) +{ + if (IS_ERR_OR_NULL(active_clevo_interface)) + return -ENODEV; + + if (!IS_ERR_OR_NULL(id_str)) + *id_str = active_clevo_interface->string_id; + + return 0; +} +EXPORT_SYMBOL(clevo_get_active_interface_id); + +static int set_enabled_cmd(u8 state) +{ + u32 cmd = 0xE0000000; + TUXEDO_INFO("Set keyboard enabled to: %d\n", state); + + if (state == 0) { + cmd |= 0x003001; + } else { + cmd |= 0x07F001; + } + + return clevo_evaluate_method(CLEVO_CMD_SET_KB_RGB_LEDS, cmd, NULL); +} + +static void set_next_color_whole_kb(void) +{ + /* "Calculate" new to-be color */ + u32 new_color_id; + u32 new_color_code; + + new_color_id = kbd_led_state.whole_kbd_color + 1; + if (new_color_id >= color_list.size) { + new_color_id = 1; // Skip black + } + new_color_code = color_list.colors[new_color_id].code; + + TUXEDO_INFO("set_next_color_whole_kb(): new_color_id: %i, new_color_code %X", + new_color_id, new_color_code); + + /* Set color on all four regions*/ + clevo_leds_set_color_extern(new_color_code); + kbd_led_state.whole_kbd_color = new_color_id; +} + +static void set_kbd_backlight_mode(u8 kbd_backlight_mode) +{ + TUXEDO_INFO("Set keyboard backlight mode on %s", kbd_backlight_modes[kbd_backlight_mode].name); + + if (!clevo_evaluate_method(CLEVO_CMD_SET_KB_RGB_LEDS, kbd_backlight_modes[kbd_backlight_mode].value, NULL)) { + // method was succesfull so update ur internal state struct + kbd_led_state.mode = kbd_backlight_mode; + } +} + +// Sysfs Interface for the keyboard backlight mode +static ssize_t list_kbd_backlight_modes_fs(struct device *child, struct device_attribute *attr, + char *buffer) +{ + return sprintf(buffer, "%d\n", kbd_led_state.mode); +} + +static ssize_t set_kbd_backlight_mode_fs(struct device *child, + struct device_attribute *attr, + const char *buffer, size_t size) +{ + unsigned int kbd_backlight_mode; + + int err = kstrtouint(buffer, 0, &kbd_backlight_mode); + if (err) { + return err; + } + + kbd_backlight_mode = clamp_t(u8, kbd_backlight_mode, 0, ARRAY_SIZE(kbd_backlight_modes) - 1); + set_kbd_backlight_mode(kbd_backlight_mode); + + return size; +} + +// Sysfs attribute file permissions and method linking +static DEVICE_ATTR(kbd_backlight_mode, 0644, list_kbd_backlight_modes_fs, set_kbd_backlight_mode_fs); + +static int kbd_backlight_mode_id_validator(const char *value, + const struct kernel_param *kbd_backlight_mode_param) +{ + int kbd_backlight_mode = 0; + + if (kstrtoint(value, 10, &kbd_backlight_mode) != 0 + || kbd_backlight_mode < 0 + || kbd_backlight_mode > (ARRAY_SIZE(kbd_backlight_modes) - 1)) { + return -EINVAL; + } + + return param_set_int(value, kbd_backlight_mode_param); +} + +static const struct kernel_param_ops param_ops_mode_ops = { + .set = kbd_backlight_mode_id_validator, + .get = param_get_int, +}; + +static u8 param_kbd_backlight_mode = CLEVO_KB_MODE_DEFAULT; +module_param_cb(kbd_backlight_mode, ¶m_ops_mode_ops, ¶m_kbd_backlight_mode, S_IRUSR); +MODULE_PARM_DESC(kbd_backlight_mode, "Set the keyboard backlight mode"); + +static void clevo_keyboard_event_callb(u32 event) +{ + u32 key_event = event; + + TUXEDO_DEBUG("Clevo event: %0#6x\n", event); + + switch (key_event) { + case CLEVO_EVENT_KB_LEDS_CYCLE_MODE: + set_next_color_whole_kb(); + break; + default: + break; + } + + if (current_driver != NULL && current_driver->input_device != NULL) { + if (!sparse_keymap_report_known_event(current_driver->input_device, key_event, 1, true)) { + TUXEDO_DEBUG("Unknown key - %d (%0#6x)\n", key_event, key_event); + } + } +} + +static void clevo_keyboard_init_device_interface(struct platform_device *dev) +{ + // Setup sysfs + if (clevo_leds_get_backlight_type() == CLEVO_KB_BACKLIGHT_TYPE_3_ZONE_RGB) { + if (device_create_file(&dev->dev, &dev_attr_kbd_backlight_mode) != 0) { + TUXEDO_ERROR("Sysfs attribute file creation failed for blinking pattern\n"); + } + else { + kbd_led_state.has_mode = 1; + } + } +} + +/** + * strstr version of dmi_match + */ +static bool dmi_string_in(enum dmi_field f, const char *str) +{ + const char *info = dmi_get_system_info(f); + + if (info == NULL || str == NULL) + return info == str; + + return strstr(info, str) != NULL; +} + +static void clevo_keyboard_init(void) +{ + bool performance_profile_set_workaround; + + kbd_led_state.mode = param_kbd_backlight_mode; + set_kbd_backlight_mode(kbd_led_state.mode); + + clevo_evaluate_method(CLEVO_CMD_SET_EVENTS_ENABLED, 0, NULL); + set_enabled_cmd(1); + + // Workaround for firmware issue not setting selected performance profile. + // Explicitly set "performance" perf. profile on init regardless of what is chosen + // for these devices (Aura, XP14, IBS14v5) + performance_profile_set_workaround = false + || dmi_string_in(DMI_BOARD_NAME, "AURA1501") + || dmi_string_in(DMI_BOARD_NAME, "EDUBOOK1502") + || dmi_string_in(DMI_BOARD_NAME, "NL5xRU") + || dmi_string_in(DMI_BOARD_NAME, "NV4XMB,ME,MZ") + || dmi_string_in(DMI_BOARD_NAME, "L140CU") + || dmi_string_in(DMI_BOARD_NAME, "NS50MU") + || dmi_string_in(DMI_BOARD_NAME, "NS50_70MU") + || dmi_string_in(DMI_BOARD_NAME, "PCX0DX") + || dmi_string_in(DMI_BOARD_NAME, "PCx0Dx_GN20") + || dmi_string_in(DMI_BOARD_NAME, "L14xMU") + ; + if (performance_profile_set_workaround) { + TUXEDO_INFO("Performance profile 'performance' set workaround applied\n"); + clevo_evaluate_method(CLEVO_CMD_OPT, 0x19000002, NULL); + } +} + +static int clevo_keyboard_probe(struct platform_device *dev) +{ + clevo_leds_init(dev); + // clevo_keyboard_init_device_interface() must come after clevo_leds_init() + // to know keyboard backlight type + clevo_keyboard_init_device_interface(dev); + clevo_keyboard_init(); + + return 0; +} + +static void clevo_keyboard_remove_device_interface(struct platform_device *dev) +{ + if (kbd_led_state.has_mode == 1) { + device_remove_file(&dev->dev, &dev_attr_kbd_backlight_mode); + } +} + +static int clevo_keyboard_remove(struct platform_device *dev) +{ + clevo_keyboard_remove_device_interface(dev); + clevo_leds_remove(dev); + return 0; +} + +static int clevo_keyboard_suspend(struct platform_device *dev, pm_message_t state) +{ + // turning the keyboard off prevents default colours showing on resume + set_enabled_cmd(0); + return 0; +} + +static int clevo_keyboard_resume(struct platform_device *dev) +{ + clevo_evaluate_method(CLEVO_CMD_SET_EVENTS_ENABLED, 0, NULL); + set_enabled_cmd(1); + return 0; +} + +static struct platform_driver platform_driver_clevo = { + .remove = clevo_keyboard_remove, + .suspend = clevo_keyboard_suspend, + .resume = clevo_keyboard_resume, + .driver = + { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static struct tuxedo_keyboard_driver clevo_keyboard_driver = { + .platform_driver = &platform_driver_clevo, + .probe = clevo_keyboard_probe, + .key_map = clevo_keymap, +}; + u32 clevo_keyboard_add_interface(struct clevo_interface_t *new_interface) { mutex_lock(&clevo_keyboard_interface_modification_lock); @@ -79,17 +391,16 @@ u32 clevo_keyboard_add_interface(struct clevo_interface_t *new_interface) // Only use wmi if there is no other current interface if (ZERO_OR_NULL_PTR(active_clevo_interface)) { pr_debug("enable wmi events\n"); - clevo_interfaces.wmi->method_call(0x46, 0, NULL); + clevo_interfaces.wmi->method_call(CLEVO_CMD_SET_EVENTS_ENABLED, 0, NULL); active_clevo_interface = clevo_interfaces.wmi; } - } else if (strcmp(new_interface->string_id, CLEVO_INTERFACE_ACPI_STRID) == 0) { clevo_interfaces.acpi = new_interface; clevo_interfaces.acpi->event_callb = clevo_keyboard_event_callb; pr_debug("enable acpi events (takes priority)\n"); - clevo_interfaces.acpi->method_call(0x46, 0, NULL); + clevo_interfaces.acpi->method_call(CLEVO_CMD_SET_EVENTS_ENABLED, 0, NULL); active_clevo_interface = clevo_interfaces.acpi; } else { // Not recognized interface @@ -124,7 +435,7 @@ u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface) tuxedo_keyboard_remove_driver(&clevo_keyboard_driver); active_clevo_interface = NULL; } - + mutex_unlock(&clevo_keyboard_interface_modification_lock); @@ -132,698 +443,4 @@ u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface) } EXPORT_SYMBOL(clevo_keyboard_remove_interface); -static struct key_entry clevo_keymap[] = { - // Keyboard backlight (RGB versions) - { KE_KEY, CLEVO_EVENT_DECREASE_BACKLIGHT, { KEY_KBDILLUMDOWN } }, - { KE_KEY, CLEVO_EVENT_INCREASE_BACKLIGHT, { KEY_KBDILLUMUP } }, - { KE_KEY, CLEVO_EVENT_TOGGLE_STATE, { KEY_KBDILLUMTOGGLE } }, - { KE_KEY, CLEVO_EVENT_NEXT_BLINKING_PATTERN, { KEY_LIGHTS_TOGGLE } }, - // Single cycle key (white only versions) - { KE_KEY, CLEVO_EVENT_CYCLE_BRIGHTNESS, { KEY_KBDILLUMUP } }, - - // Touchpad - // The weirdly named touchpad toggle key that is implemented as KEY_F21 "everywhere" - // (instead of KEY_TOUCHPAD_TOGGLE or on/off) - // Most "new" devices just provide one toggle event - { KE_KEY, CLEVO_EVENT_TOUCHPAD_TOGGLE, { KEY_F21 } }, - // Some "old" devices produces on/off events - { KE_KEY, CLEVO_EVENT_TOUCHPAD_OFF, { KEY_F21 } }, - { KE_KEY, CLEVO_EVENT_TOUCHPAD_ON, { KEY_F21 } }, - // The alternative key events (currently not used) - // { KE_KEY, CLEVO_EVENT_TOUCHPAD_OFF, { KEY_TOUCHPAD_OFF } }, - // { KE_KEY, CLEVO_EVENT_TOUCHPAD_ON, { KEY_TOUCHPAD_ON } }, - // { KE_KEY, CLEVO_EVENT_TOUCHPAD_TOGGLE, { KEY_TOUCHPAD_TOGGLE } }, - - // Rfkill still needed by some devices - { KE_KEY, CLEVO_EVENT_RFKILL1, { KEY_RFKILL } }, - { KE_IGNORE, CLEVO_EVENT_RFKILL2, { KEY_RFKILL } }, // Older rfkill event - // Note: Volume events need to be ignored as to not interfere with built-in functionality - { KE_IGNORE, 0xfa, { KEY_UNKNOWN } }, // Appears by volume up/down - { KE_IGNORE, 0xfb, { KEY_UNKNOWN } }, // Appears by mute toggle - - { KE_END, 0 } -}; - -#define BRIGHTNESS_STEP 25 - -// Keyboard struct -struct kbd_led_state_t { - u8 has_extra; - u8 enabled; - - struct { - u32 left; - u32 center; - u32 right; - u32 extra; - } color; - - u8 brightness; - u8 blinking_pattern; - u8 whole_kbd_color; -}; - -struct blinking_pattern_t { - u8 key; - u32 value; - const char *const name; -}; - - -static int blinking_pattern_id_validator(const char *value, - const struct kernel_param *blinking_pattern_param); -static const struct kernel_param_ops param_ops_mode_ops = { - .set = blinking_pattern_id_validator, - .get = param_get_int, -}; - -static uint param_color_left = KB_COLOR_DEFAULT; -module_param_named(color_left, param_color_left, uint, S_IRUSR); -MODULE_PARM_DESC(color_left, "Color for the Left Region"); - -static uint param_color_center = KB_COLOR_DEFAULT; -module_param_named(color_center, param_color_center, uint, S_IRUSR); -MODULE_PARM_DESC(color_center, "Color for the Center Region"); - -static uint param_color_right = KB_COLOR_DEFAULT; -module_param_named(color_right, param_color_right, uint, S_IRUSR); -MODULE_PARM_DESC(color_right, "Color for the Right Region"); - -static uint param_color_extra = KB_COLOR_DEFAULT; -module_param_named(color_extra, param_color_extra, uint, S_IRUSR); -MODULE_PARM_DESC(color_extra, "Color for the Extra Region"); - -static ushort param_blinking_pattern = DEFAULT_BLINKING_PATTERN; -module_param_cb(mode, ¶m_ops_mode_ops, ¶m_blinking_pattern, S_IRUSR); -MODULE_PARM_DESC(mode, "Set the keyboard backlight blinking pattern"); - -static bool param_state = true; -module_param_named(state, param_state, bool, S_IRUSR); -MODULE_PARM_DESC(state, - "Set the State of the Keyboard TRUE = ON | FALSE = OFF"); - -static struct kbd_led_state_t kbd_led_state = { - .has_extra = 0, - .enabled = 1, - .color = { - .left = KB_COLOR_DEFAULT, .center = KB_COLOR_DEFAULT, - .right = KB_COLOR_DEFAULT, .extra = KB_COLOR_DEFAULT - }, - .brightness = BRIGHTNESS_DEFAULT, - .blinking_pattern = DEFAULT_BLINKING_PATTERN, - .whole_kbd_color = 7 -}; - -static struct blinking_pattern_t blinking_patterns[] = { - { .key = 0,.value = 0,.name = "CUSTOM"}, - { .key = 1,.value = 0x1002a000,.name = "BREATHE"}, - { .key = 2,.value = 0x33010000,.name = "CYCLE"}, - { .key = 3,.value = 0x80000000,.name = "DANCE"}, - { .key = 4,.value = 0xA0000000,.name = "FLASH"}, - { .key = 5,.value = 0x70000000,.name = "RANDOM_COLOR"}, - { .key = 6,.value = 0x90000000,.name = "TEMPO"}, - { .key = 7,.value = 0xB0000000,.name = "WAVE"} -}; - -// Sysfs Interface Methods -// Sysfs Interface for the keyboard state (ON / OFF) -static ssize_t show_state_fs(struct device *child, - struct device_attribute *attr, char *buffer) -{ - return sprintf(buffer, "%d\n", kbd_led_state.enabled); -} - -// Sysfs Interface for the color of the left side (Color as hexvalue) -static ssize_t show_color_left_fs(struct device *child, - struct device_attribute *attr, char *buffer) -{ - return sprintf(buffer, "%06x\n", kbd_led_state.color.left); -} - -// Sysfs Interface for the color of the center (Color as hexvalue) -static ssize_t show_color_center_fs(struct device *child, - struct device_attribute *attr, char *buffer) -{ - return sprintf(buffer, "%06x\n", kbd_led_state.color.center); -} - -// Sysfs Interface for the color of the right side (Color as hexvalue) -static ssize_t show_color_right_fs(struct device *child, - struct device_attribute *attr, char *buffer) -{ - return sprintf(buffer, "%06x\n", kbd_led_state.color.right); -} - -// Sysfs Interface for the color of the extra region (Color as hexvalue) -static ssize_t show_color_extra_fs(struct device *child, - struct device_attribute *attr, char *buffer) -{ - return sprintf(buffer, "%06x\n", kbd_led_state.color.extra); -} - -// Sysfs Interface for the keyboard brightness (unsigned int) -static ssize_t show_brightness_fs(struct device *child, - struct device_attribute *attr, char *buffer) -{ - return sprintf(buffer, "%d\n", kbd_led_state.brightness); -} - -// Sysfs Interface for the backlight blinking pattern -static ssize_t show_blinking_patterns_fs(struct device *child, struct device_attribute *attr, - char *buffer) -{ - return sprintf(buffer, "%d\n", kbd_led_state.blinking_pattern); -} - -// Sysfs Interface for if the keyboard has extra region -static ssize_t show_hasextra_fs(struct device *child, - struct device_attribute *attr, char *buffer) -{ - return sprintf(buffer, "%d\n", kbd_led_state.has_extra); -} - -u32 clevo_evaluate_method(u8 cmd, u32 arg, u32 *result) -{ - if (IS_ERR_OR_NULL(active_clevo_interface)) { - pr_err("clevo_keyboard: no active interface while attempting cmd %02x arg %08x\n", cmd, arg); - return -ENODEV; - } - return active_clevo_interface->method_call(cmd, arg, result); -} -EXPORT_SYMBOL(clevo_evaluate_method); - -u32 clevo_get_active_interface_id(char **id_str) -{ - if (IS_ERR_OR_NULL(active_clevo_interface)) - return -ENODEV; - - if (!IS_ERR_OR_NULL(id_str)) - *id_str = active_clevo_interface->string_id; - - return 0; -} -EXPORT_SYMBOL(clevo_get_active_interface_id); - -static void set_brightness(u8 brightness) -{ - TUXEDO_INFO("Set brightness on %d", brightness); - if (!clevo_evaluate_method - (CLEVO_METHOD_ID_SET_KB_LEDS, 0xF4000000 | brightness, NULL)) { - kbd_led_state.brightness = brightness; - } -} - -static ssize_t set_brightness_fs(struct device *child, - struct device_attribute *attr, - const char *buffer, size_t size) -{ - unsigned int val; - // hier unsigned? - - int err = kstrtouint(buffer, 0, &val); - if (err) { - return err; - } - - val = clamp_t(u8, val, BRIGHTNESS_MIN, BRIGHTNESS_MAX); - set_brightness(val); - - return size; -} - -static int set_enabled_cmd(u8 state) -{ - u32 cmd = 0xE0000000; - TUXEDO_INFO("Set keyboard enabled to: %d\n", state); - - if (state == 0) { - cmd |= 0x003001; - } else { - cmd |= 0x07F001; - } - - return clevo_evaluate_method(CLEVO_METHOD_ID_SET_KB_LEDS, cmd, NULL); -} - -static void set_enabled(u8 state) -{ - if (!set_enabled_cmd(state)) { - kbd_led_state.enabled = state; - } -} - -static ssize_t set_state_fs(struct device *child, struct device_attribute *attr, - const char *buffer, size_t size) -{ - unsigned int state; - - int err = kstrtouint(buffer, 0, &state); - if (err) { - return err; - } - - state = clamp_t(u8, state, 0, 1); - - set_enabled(state); - - return size; -} - -static int set_color(u32 region, u32 color) -{ - u32 cset = - ((color & 0x0000FF) << 16) | ((color & 0xFF0000) >> 8) | - ((color & 0x00FF00) >> 8); - u32 clevo_submethod_arg = region | cset; - - TUXEDO_DEBUG("Set Color '%08x' for region '%08x'", color, region); - - return clevo_evaluate_method(CLEVO_METHOD_ID_SET_KB_LEDS, clevo_submethod_arg, NULL); -} -static int set_color_code_region(u32 region, u32 colorcode) -{ - int err; - if (0 == (err = set_color(region, colorcode))) { - // after succesfully setting color, update our state struct - // depending on which region was changed - switch (region) { - case REGION_LEFT: - kbd_led_state.color.left = colorcode; - break; - case REGION_CENTER: - kbd_led_state.color.center = colorcode; - break; - case REGION_RIGHT: - kbd_led_state.color.right = colorcode; - break; - case REGION_EXTRA: - kbd_led_state.color.extra = colorcode; - break; - } - } - - return err; -} - -static int set_color_string_region(const char *color_string, size_t size, u32 region) -{ - u32 colorcode; - int err = kstrtouint(color_string, 0, &colorcode); - - if (err) { - return err; - } - - if (!set_color(region, colorcode)) { - // after succesfully setting color, update our state struct - // depending on which region was changed - switch (region) { - case REGION_LEFT: - kbd_led_state.color.left = colorcode; - break; - case REGION_CENTER: - kbd_led_state.color.center = colorcode; - break; - case REGION_RIGHT: - kbd_led_state.color.right = colorcode; - break; - case REGION_EXTRA: - kbd_led_state.color.extra = colorcode; - break; - } - } - - return size; -} - -static ssize_t set_color_left_fs(struct device *child, - struct device_attribute *attr, - const char *color_string, size_t size) -{ - return set_color_string_region(color_string, size, REGION_LEFT); -} - -static ssize_t set_color_center_fs(struct device *child, - struct device_attribute *attr, - const char *color_string, size_t size) -{ - return set_color_string_region(color_string, size, REGION_CENTER); -} - -static ssize_t set_color_right_fs(struct device *child, - struct device_attribute *attr, - const char *color_string, size_t size) -{ - return set_color_string_region(color_string, size, REGION_RIGHT); -} - -static ssize_t set_color_extra_fs(struct device *child, - struct device_attribute *attr, - const char *color_string, size_t size) -{ - return set_color_string_region(color_string, size, REGION_EXTRA); -} - -static int set_next_color_whole_kb(void) -{ - /* "Calculate" new to-be color */ - u32 new_color_id; - u32 new_color_code; - - new_color_id = kbd_led_state.whole_kbd_color + 1; - if (new_color_id >= color_list.size) { - new_color_id = 0; - } - new_color_code = color_list.colors[new_color_id].code; - - TUXEDO_INFO("set_next_color_whole_kb(): new_color_id: %i, new_color_code %X", - new_color_id, new_color_code); - - /* Set color on all four regions*/ - set_color_code_region(REGION_LEFT, new_color_code); - set_color_code_region(REGION_CENTER, new_color_code); - set_color_code_region(REGION_RIGHT, new_color_code); - set_color_code_region(REGION_EXTRA, new_color_code); - - kbd_led_state.whole_kbd_color = new_color_id; - - return 0; -} - -static void set_blinking_pattern(u8 blinkling_pattern) -{ - TUXEDO_INFO("set_mode on %s", blinking_patterns[blinkling_pattern].name); - - if (!clevo_evaluate_method(CLEVO_METHOD_ID_SET_KB_LEDS, blinking_patterns[blinkling_pattern].value, NULL)) { - // method was succesfull so update ur internal state struct - kbd_led_state.blinking_pattern = blinkling_pattern; - } - - if (blinkling_pattern == 0) { // 0 is the "custom" blinking pattern - - // so just set all regions to the stored colors - set_color(REGION_LEFT, kbd_led_state.color.left); - set_color(REGION_CENTER, kbd_led_state.color.center); - set_color(REGION_RIGHT, kbd_led_state.color.right); - - if (kbd_led_state.has_extra == 1) { - set_color(REGION_EXTRA, kbd_led_state.color.extra); - } - } -} - -static ssize_t set_blinking_pattern_fs(struct device *child, - struct device_attribute *attr, - const char *buffer, size_t size) -{ - unsigned int blinking_pattern; - - int err = kstrtouint(buffer, 0, &blinking_pattern); - if (err) { - return err; - } - - blinking_pattern = clamp_t(u8, blinking_pattern, 0, ARRAY_SIZE(blinking_patterns) - 1); - set_blinking_pattern(blinking_pattern); - - return size; -} - -static int blinking_pattern_id_validator(const char *value, - const struct kernel_param *blinking_pattern_param) -{ - int blinking_pattern = 0; - - if (kstrtoint(value, 10, &blinking_pattern) != 0 - || blinking_pattern < 0 - || blinking_pattern > (ARRAY_SIZE(blinking_patterns) - 1)) { - return -EINVAL; - } - - return param_set_int(value, blinking_pattern_param); -} - -static int brightness_validator(const char *value, - const struct kernel_param *brightness_param) -{ - int brightness = 0; - - if (kstrtoint(value, 10, &brightness) != 0 - || brightness < BRIGHTNESS_MIN - || brightness > BRIGHTNESS_MAX) { - return -EINVAL; - } - - return param_set_int(value, brightness_param); -} - -void clevo_keyboard_event_callb(u32 event) -{ - u32 key_event = event; - - // TUXEDO_DEBUG("clevo event: %0#6x\n", event); - - switch (key_event) { - case CLEVO_EVENT_DECREASE_BACKLIGHT: - if (kbd_led_state.brightness == BRIGHTNESS_MIN - || (kbd_led_state.brightness - 25) < BRIGHTNESS_MIN) { - set_brightness(BRIGHTNESS_MIN); - } else { - set_brightness(kbd_led_state.brightness - 25); - } - - break; - - case CLEVO_EVENT_INCREASE_BACKLIGHT: - if (kbd_led_state.brightness == BRIGHTNESS_MAX - || (kbd_led_state.brightness + 25) > BRIGHTNESS_MAX) { - set_brightness(BRIGHTNESS_MAX); - } else { - set_brightness(kbd_led_state.brightness + 25); - } - - break; - -// case CLEVO_EVENT_NEXT_BLINKING_PATTERN: -// set_blinking_pattern((kbd_led_state.blinking_pattern + 1) > -// (ARRAY_SIZE(blinking_patterns) - 1) ? 0 : (kbd_led_state.blinking_pattern + 1)); -// break; - - case CLEVO_EVENT_NEXT_BLINKING_PATTERN: - set_next_color_whole_kb(); - break; - - case CLEVO_EVENT_TOGGLE_STATE: - set_enabled(kbd_led_state.enabled == 0 ? 1 : 0); - break; - - default: - break; - } - - if (current_driver != NULL && current_driver->input_device != NULL) { - if (!sparse_keymap_report_known_event( - current_driver->input_device, key_event, 1, true)) { - TUXEDO_DEBUG("Unknown key - %d (%0#6x)\n", key_event, - key_event); - } - } -} - -// Sysfs attribute file permissions and method linking -static DEVICE_ATTR(state, 0644, show_state_fs, set_state_fs); -static DEVICE_ATTR(color_left, 0644, show_color_left_fs, set_color_left_fs); -static DEVICE_ATTR(color_center, 0644, show_color_center_fs, - set_color_center_fs); -static DEVICE_ATTR(color_right, 0644, show_color_right_fs, set_color_right_fs); -static DEVICE_ATTR(color_extra, 0644, show_color_extra_fs, set_color_extra_fs); -static DEVICE_ATTR(brightness, 0644, show_brightness_fs, set_brightness_fs); -static DEVICE_ATTR(mode, 0644, show_blinking_patterns_fs, set_blinking_pattern_fs); -static DEVICE_ATTR(extra, 0444, show_hasextra_fs, NULL); - -static void clevo_keyboard_init_device_interface(struct platform_device *dev) -{ - // Setup sysfs - if (device_create_file(&dev->dev, &dev_attr_state) != 0) { - TUXEDO_ERROR("Sysfs attribute file creation failed for state\n"); - } - - if (device_create_file - (&dev->dev, &dev_attr_color_left) != 0) { - TUXEDO_ERROR - ("Sysfs attribute file creation failed for color left\n"); - } - - if (device_create_file - (&dev->dev, &dev_attr_color_center) != 0) { - TUXEDO_ERROR - ("Sysfs attribute file creation failed for color center\n"); - } - - if (device_create_file - (&dev->dev, &dev_attr_color_right) != 0) { - TUXEDO_ERROR - ("Sysfs attribute file creation failed for color right\n"); - } - - if (set_color(REGION_EXTRA, KB_COLOR_DEFAULT) != 0) { - TUXEDO_DEBUG("Keyboard does not support EXTRA Color"); - kbd_led_state.has_extra = 0; - } else { - kbd_led_state.has_extra = 1; - if (device_create_file - (&dev->dev, - &dev_attr_color_extra) != 0) { - TUXEDO_ERROR - ("Sysfs attribute file creation failed for color extra\n"); - } - - set_color(REGION_EXTRA, param_color_extra); - } - - if (device_create_file(&dev->dev, &dev_attr_extra) != - 0) { - TUXEDO_ERROR - ("Sysfs attribute file creation failed for extra information flag\n"); - } - - if (device_create_file(&dev->dev, &dev_attr_mode) != - 0) { - TUXEDO_ERROR("Sysfs attribute file creation failed for blinking pattern\n"); - } - - if (device_create_file - (&dev->dev, &dev_attr_brightness) != 0) { - TUXEDO_ERROR - ("Sysfs attribute file creation failed for brightness\n"); - } - -} - -void clevo_keyboard_write_state(void) -{ - // Note: - // - set_blinking_pattern also writes colors - // - set_brightness, set_enabled, set_blinking_pattern - // still also update state - set_blinking_pattern(kbd_led_state.blinking_pattern); - set_brightness(kbd_led_state.brightness); - set_enabled(kbd_led_state.enabled); -} - -/** - * strstr version of dmi_match - */ -static bool dmi_string_in(enum dmi_field f, const char *str) -{ - const char *info = dmi_get_system_info(f); - - if (info == NULL || str == NULL) - return info == str; - - return strstr(info, str) != NULL; -} - -int clevo_keyboard_init(void) -{ - bool performance_profile_set_workaround; - - // Init state from params - kbd_led_state.color.left = param_color_left; - kbd_led_state.color.center = param_color_center; - kbd_led_state.color.right = param_color_right; - kbd_led_state.color.extra = param_color_extra; - - kbd_led_state.blinking_pattern = param_blinking_pattern; - - if (param_brightness > BRIGHTNESS_MAX) param_brightness = BRIGHTNESS_DEFAULT; - kbd_led_state.brightness = param_brightness; - - kbd_led_state.enabled = param_state; - - clevo_keyboard_write_state(); - - // Workaround for firmware issue not setting selected performance profile. - // Explicitly set "performance" perf. profile on init regardless of what is chosen - // for these devices (Aura, XP14, IBS14v5) - performance_profile_set_workaround = false - || dmi_string_in(DMI_BOARD_NAME, "AURA1501") - || dmi_string_in(DMI_BOARD_NAME, "EDUBOOK1502") - || dmi_string_in(DMI_BOARD_NAME, "NL5xRU") - || dmi_string_in(DMI_BOARD_NAME, "NV4XMB,ME,MZ") - || dmi_string_in(DMI_BOARD_NAME, "L140CU") - || dmi_string_in(DMI_BOARD_NAME, "NS50MU") - || dmi_string_in(DMI_BOARD_NAME, "NS50_70MU") - || dmi_string_in(DMI_BOARD_NAME, "PCX0DX") - || dmi_string_in(DMI_BOARD_NAME, "PCx0Dx_GN20") - || dmi_string_in(DMI_BOARD_NAME, "L14xMU") - ; - if (performance_profile_set_workaround) { - TUXEDO_INFO("Performance profile 'performance' set workaround applied\n"); - clevo_evaluate_method(0x79, 0x19000002, NULL); - } - - return 0; -} - -static int clevo_keyboard_probe(struct platform_device *dev) -{ - clevo_keyboard_init_device_interface(dev); - clevo_keyboard_init(); - - return 0; -} - -static void clevo_keyboard_remove_device_interface(struct platform_device *dev) -{ - device_remove_file(&dev->dev, &dev_attr_state); - device_remove_file(&dev->dev, &dev_attr_color_left); - device_remove_file(&dev->dev, &dev_attr_color_center); - device_remove_file(&dev->dev, &dev_attr_color_right); - device_remove_file(&dev->dev, &dev_attr_extra); - device_remove_file(&dev->dev, &dev_attr_mode); - device_remove_file(&dev->dev, &dev_attr_brightness); - - if (kbd_led_state.has_extra == 1) { - device_remove_file(&dev->dev, &dev_attr_color_extra); - } -} - -static int clevo_keyboard_remove(struct platform_device *dev) -{ - clevo_keyboard_remove_device_interface(dev); - return 0; -} - -static int clevo_keyboard_suspend(struct platform_device *dev, pm_message_t state) -{ - // turning the keyboard off prevents default colours showing on resume - set_enabled_cmd(0); - return 0; -} - -static int clevo_keyboard_resume(struct platform_device *dev) -{ - clevo_evaluate_method(CLEVO_METHOD_ID_GET_AP, 0, NULL); - - clevo_keyboard_write_state(); - - return 0; -} - -static struct platform_driver platform_driver_clevo = { - .remove = clevo_keyboard_remove, - .suspend = clevo_keyboard_suspend, - .resume = clevo_keyboard_resume, - .driver = - { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -struct tuxedo_keyboard_driver clevo_keyboard_driver = { - .platform_driver = &platform_driver_clevo, - .probe = clevo_keyboard_probe, - .key_map = clevo_keymap, -}; +#endif // CLEVO_KEYBOARD_H diff --git a/src/clevo_leds.h b/src/clevo_leds.h new file mode 100644 index 0000000..18f2ab7 --- /dev/null +++ b/src/clevo_leds.h @@ -0,0 +1,424 @@ +/*! + * Copyright (c) 2018-2020 TUXEDO Computers GmbH + * + * This file is part of tuxedo-keyboard. + * + * tuxedo-keyboard is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. If not, see . + */ + +#ifndef CLEVO_LEDS_H +#define CLEVO_LEDS_H + +#include +#include +#include + +enum clevo_kb_backlight_types { + CLEVO_KB_BACKLIGHT_TYPE_NONE = 0x00, + CLEVO_KB_BACKLIGHT_TYPE_FIXED_COLOR = 0x01, + CLEVO_KB_BACKLIGHT_TYPE_3_ZONE_RGB = 0x02, + CLEVO_KB_BACKLIGHT_TYPE_1_ZONE_RGB = 0x06, + CLEVO_KB_BACKLIGHT_TYPE_PER_KEY_RGB = 0xf3 +}; + +int clevo_leds_init(struct platform_device *dev); +int clevo_leds_remove(struct platform_device *dev); +enum clevo_kb_backlight_types clevo_leds_get_backlight_type(void); +void clevo_leds_set_brightness_extern(enum led_brightness brightness); +void clevo_leds_set_color_extern(u32 color); + +// TODO The following should go into a seperate .c file, but for this to work more reworking is required in the tuxedo_keyboard structure. + +#include "clevo_leds.h" + +#include "clevo_interfaces.h" + +#include + +#define CLEVO_KBD_BRIGHTNESS_MAX 0xff +#define CLEVO_KBD_BRIGHTNESS_DEFAULT 0x00 + +#define CLEVO_KBD_BRIGHTNESS_WHITE_MAX 0x02 // White only keyboards can only be off, half, or full brightness +#define CLEVO_KBD_BRIGHTNESS_WHITE_DEFAULT 0x00 + +#define CLEVO_KBD_BRIGHTNESS_WHITE_MAX_5 0x05 // Devices <= Intel 7th gen had a different white control with 5 brightness values + off +#define CLEVO_KBD_BRIGHTNESS_WHITE_MAX_5_DEFAULT 0x00 + +#define CLEVO_KB_COLOR_DEFAULT_RED 0xff +#define CLEVO_KB_COLOR_DEFAULT_GREEN 0xff +#define CLEVO_KB_COLOR_DEFAULT_BLUE 0xff +#define CLEVO_KB_COLOR_DEFAULT ((CLEVO_KB_COLOR_DEFAULT_RED << 16) + (CLEVO_KB_COLOR_DEFAULT_GREEN << 8) + CLEVO_KB_COLOR_DEFAULT_BLUE) + +static enum clevo_kb_backlight_types clevo_kb_backlight_type = CLEVO_KB_BACKLIGHT_TYPE_NONE; +static bool leds_initialized = false; + +static int clevo_evaluate_set_white_brightness(u8 brightness) +{ + pr_debug("Set white brightness on %d\n", brightness); + + return clevo_evaluate_method (CLEVO_CMD_SET_KB_WHITE_LEDS, brightness, NULL); +} + +static int clevo_evaluate_set_rgb_brightness(u8 brightness) +{ + pr_debug("Set RGB brightness on %d\n", brightness); + + return clevo_evaluate_method (CLEVO_CMD_SET_KB_RGB_LEDS, CLEVO_CMD_SET_KB_LEDS_SUB_RGB_BRIGHTNESS | brightness, NULL); +} + +static int clevo_evaluate_set_rgb_color(u32 zone, u32 color) +{ + u32 cset = ((color & 0x0000FF) << 16) | ((color & 0xFF0000) >> 8) | ((color & 0x00FF00) >> 8); + u32 clevo_submethod_arg = zone | cset; + + pr_debug("Set Color 0x%08x for region 0x%08x\n", color, zone); + + return clevo_evaluate_method(CLEVO_CMD_SET_KB_RGB_LEDS, clevo_submethod_arg, NULL); +} + +static void clevo_leds_set_brightness(struct led_classdev *led_cdev __always_unused, enum led_brightness brightness) { + int ret = clevo_evaluate_set_white_brightness(brightness); + if (ret) { + pr_debug("clevo_leds_set_brightness(): clevo_evaluate_set_white_brightness() failed\n"); + return; + } + led_cdev->brightness = brightness; +} + +/*static void clevo_leds_set_brightness_mc(struct led_classdev *led_cdev, enum led_brightness brightness) { + int ret; + u32 zone, color; + struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); + + ret = clevo_evaluate_set_rgb_brightness(CLEVO_KBD_BRIGHTNESS_MAX); + if (ret) { + pr_debug("clevo_leds_set_brightness_mc(): clevo_evaluate_set_rgb_brightness() failed\n"); + return; + } + + zone = mcled_cdev->subled_info[0].channel; + + led_mc_calc_color_components(mcled_cdev, brightness); + color = (mcled_cdev->subled_info[0].brightness << 16) + + (mcled_cdev->subled_info[1].brightness << 8) + + mcled_cdev->subled_info[2].brightness; + + ret = clevo_evaluate_set_rgb_color(zone, color); + if (ret) { + pr_debug("clevo_leds_set_brightness_mc(): clevo_evaluate_set_rgb_color() failed\n"); + return; + } + led_cdev->brightness = brightness; +}*/ + +// Temprary fix for KDE: KDE does only set one kbd_backlight brightness value, this version of the +// function uses clevos built in brightness setting to set the whole keyboard brightness at once. +// -> use clevo_evaluate_set_rgb_brightness() to set overall brightness via firmware instead of scaling +// the RGB values +// -> update all clevo_mcled_cdevs brightness levels to refect that the firmware method sets the +// the whole keyboard brightness and not just one zone +// This is a temporary fix until KDE handles multiple keyboard backlights correctly +static struct led_classdev_mc clevo_mcled_cdevs[3]; // forward declaration +static void clevo_leds_set_brightness_mc(struct led_classdev *led_cdev, enum led_brightness brightness) { + int ret; + u32 zone, color; + struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); + + ret = clevo_evaluate_set_rgb_brightness(brightness); + if (ret) { + pr_debug("clevo_leds_set_brightness_mc(): clevo_evaluate_set_rgb_brightness() failed\n"); + return; + } + clevo_mcled_cdevs[0].led_cdev.brightness = brightness; + clevo_mcled_cdevs[1].led_cdev.brightness = brightness; + clevo_mcled_cdevs[2].led_cdev.brightness = brightness; + + zone = mcled_cdev->subled_info[0].channel; + + color = (mcled_cdev->subled_info[0].intensity << 16) + + (mcled_cdev->subled_info[1].intensity << 8) + + mcled_cdev->subled_info[2].intensity; + + ret = clevo_evaluate_set_rgb_color(zone, color); + if (ret) { + pr_debug("clevo_leds_set_brightness_mc(): clevo_evaluate_set_rgb_color() failed\n"); + } +} + +static struct led_classdev clevo_led_cdev = { + .name = "white:" LED_FUNCTION_KBD_BACKLIGHT, + .max_brightness = CLEVO_KBD_BRIGHTNESS_WHITE_MAX, + .brightness_set = &clevo_leds_set_brightness, + .brightness = CLEVO_KBD_BRIGHTNESS_WHITE_DEFAULT +}; + +static struct mc_subled clevo_mcled_cdevs_subleds[3][3] = { + { + { + .color_index = LED_COLOR_ID_RED, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_RED, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_0 + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_GREEN, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_0 + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_BLUE, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_0 + } + }, + { + { + .color_index = LED_COLOR_ID_RED, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_RED, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_1 + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_GREEN, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_1 + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_BLUE, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_1 + } + }, + { + { + .color_index = LED_COLOR_ID_RED, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_RED, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_2 + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_GREEN, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_2 + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .intensity = CLEVO_KB_COLOR_DEFAULT_BLUE, + .channel = CLEVO_CMD_SET_KB_LEDS_SUB_RGB_ZONE_2 + } + } +}; + +static struct led_classdev_mc clevo_mcled_cdevs[3] = { + { + .led_cdev.name = "rgb:" LED_FUNCTION_KBD_BACKLIGHT, + .led_cdev.max_brightness = CLEVO_KBD_BRIGHTNESS_MAX, + .led_cdev.brightness_set = &clevo_leds_set_brightness_mc, + .led_cdev.brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .num_colors = 3, + .subled_info = clevo_mcled_cdevs_subleds[0] + }, + { + .led_cdev.name = "rgb:" LED_FUNCTION_KBD_BACKLIGHT, + .led_cdev.max_brightness = CLEVO_KBD_BRIGHTNESS_MAX, + .led_cdev.brightness_set = &clevo_leds_set_brightness_mc, + .led_cdev.brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .num_colors = 3, + .subled_info = clevo_mcled_cdevs_subleds[1] + }, + { + .led_cdev.name = "rgb:" LED_FUNCTION_KBD_BACKLIGHT, + .led_cdev.max_brightness = CLEVO_KBD_BRIGHTNESS_MAX, + .led_cdev.brightness_set = &clevo_leds_set_brightness_mc, + .led_cdev.brightness = CLEVO_KBD_BRIGHTNESS_DEFAULT, + .num_colors = 3, + .subled_info = clevo_mcled_cdevs_subleds[2] + } +}; + +int clevo_leds_init(struct platform_device *dev) +{ + int ret; + u32 status; + union acpi_object *result; + u32 result_fallback; + + status = clevo_evaluate_method2(CLEVO_CMD_GET_SPECS, 0, &result); + if (!status) { + if (result->type == ACPI_TYPE_BUFFER) { + pr_debug("CLEVO_CMD_GET_SPECS result->buffer.pointer[0x0f]: 0x%02x\n", result->buffer.pointer[0x0f]); + clevo_kb_backlight_type = result->buffer.pointer[0x0f]; + } + else { + pr_err("CLEVO_CMD_GET_SPECS does not exist on this device or return value has wrong type, trying CLEVO_CMD_GET_BIOS_FEATURES\n"); + status = -EINVAL; + } + ACPI_FREE(result); + } + else { + pr_notice("CLEVO_CMD_GET_SPECS does not exist on this device or failed, trying CLEVO_CMD_GET_BIOS_FEATURES_1\n"); + } + + if (status || clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_NONE) { + // check for devices <= Intel 8th gen (only white only, 3 zone RGB, or no backlight on these devices) + status = clevo_evaluate_method(CLEVO_CMD_GET_BIOS_FEATURES_1, 0, &result_fallback); + if (!status) { + pr_debug("CLEVO_CMD_GET_BIOS_FEATURES_1 result_fallback: 0x%08x\n", result_fallback); + if (result_fallback & CLEVO_CMD_GET_BIOS_FEATURES_1_SUB_3_ZONE_RGB_KB) { + clevo_kb_backlight_type = CLEVO_KB_BACKLIGHT_TYPE_3_ZONE_RGB; + } + else if (result_fallback & CLEVO_CMD_GET_BIOS_FEATURES_1_SUB_WHITE_ONLY_KB) { + clevo_kb_backlight_type = CLEVO_KB_BACKLIGHT_TYPE_FIXED_COLOR; + + status = clevo_evaluate_method(CLEVO_CMD_GET_BIOS_FEATURES_2, 0, &result_fallback); + if (!status) { + pr_debug("CLEVO_CMD_GET_BIOS_FEATURES_2 result_fallback: 0x%08x\n", result_fallback); + if (result_fallback & CLEVO_CMD_GET_BIOS_FEATURES_2_SUB_WHITE_ONLY_KB_MAX_5) { + clevo_led_cdev.max_brightness = CLEVO_KBD_BRIGHTNESS_WHITE_MAX_5; + clevo_led_cdev.brightness = CLEVO_KBD_BRIGHTNESS_WHITE_MAX_5_DEFAULT; + } + } + else { + pr_notice("CLEVO_CMD_GET_BIOS_FEATURES_2 does not exist on this device or failed\n"); + } + } + } + else { + pr_notice("CLEVO_CMD_GET_BIOS_FEATURES_1 does not exist on this device or failed\n"); + } + } + pr_debug("Keyboard backlight type: 0x%02x\n", clevo_kb_backlight_type); + + if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_FIXED_COLOR) + clevo_leds_set_brightness_extern(clevo_led_cdev.brightness); + else + clevo_leds_set_color_extern(CLEVO_KB_COLOR_DEFAULT); + + if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_FIXED_COLOR) { + pr_debug("Registering fixed color leds interface\n"); + ret = led_classdev_register(&dev->dev, &clevo_led_cdev); + if (ret) { + pr_err("Registering fixed color leds interface failed\n"); + return ret; + } + } + else if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + pr_debug("Registering single zone rgb leds interface\n"); + ret = devm_led_classdev_multicolor_register(&dev->dev, &clevo_mcled_cdevs[0]); + if (ret) { + pr_err("Registering single zone rgb leds interface failed\n"); + return ret; + } + } + else if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_3_ZONE_RGB) { + pr_debug("Registering three zone rgb leds interface\n"); + ret = devm_led_classdev_multicolor_register(&dev->dev, &clevo_mcled_cdevs[0]); + if (ret) { + pr_err("Registering three zone rgb zone 0 leds interface failed\n"); + return ret; + } + ret = devm_led_classdev_multicolor_register(&dev->dev, &clevo_mcled_cdevs[1]); + if (ret) { + pr_err("Registering three zone rgb zone 1 leds interface failed\n"); + devm_led_classdev_multicolor_unregister(&dev->dev, &clevo_mcled_cdevs[0]); + return ret; + } + ret = devm_led_classdev_multicolor_register(&dev->dev, &clevo_mcled_cdevs[2]); + if (ret) { + pr_err("Registering three zone rgb zone 2 leds interface failed\n"); + devm_led_classdev_multicolor_unregister(&dev->dev, &clevo_mcled_cdevs[0]); + devm_led_classdev_multicolor_unregister(&dev->dev, &clevo_mcled_cdevs[1]); + return ret; + } + } + + leds_initialized = true; + return 0; +} +EXPORT_SYMBOL(clevo_leds_init); + +int clevo_leds_remove(struct platform_device *dev) { + if (leds_initialized) { + if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_FIXED_COLOR) { + led_classdev_unregister(&clevo_led_cdev); + } + else if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + devm_led_classdev_multicolor_unregister(&dev->dev, &clevo_mcled_cdevs[0]); + } + else if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_3_ZONE_RGB) { + devm_led_classdev_multicolor_unregister(&dev->dev, &clevo_mcled_cdevs[0]); + devm_led_classdev_multicolor_unregister(&dev->dev, &clevo_mcled_cdevs[1]); + devm_led_classdev_multicolor_unregister(&dev->dev, &clevo_mcled_cdevs[2]); + } + } + + leds_initialized = false; + + return 0; +} +EXPORT_SYMBOL(clevo_leds_remove); + +enum clevo_kb_backlight_types clevo_leds_get_backlight_type() { + return clevo_kb_backlight_type; +} +EXPORT_SYMBOL(clevo_leds_get_backlight_type); + +void clevo_leds_set_brightness_extern(enum led_brightness brightness) { + if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_FIXED_COLOR) { + clevo_led_cdev.brightness_set(&clevo_led_cdev, brightness); + } + else if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + clevo_mcled_cdevs[0].led_cdev.brightness_set(&clevo_mcled_cdevs[0].led_cdev, brightness); + } + else if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_3_ZONE_RGB) { + clevo_mcled_cdevs[0].led_cdev.brightness_set(&clevo_mcled_cdevs[0].led_cdev, brightness); + clevo_mcled_cdevs[1].led_cdev.brightness_set(&clevo_mcled_cdevs[1].led_cdev, brightness); + clevo_mcled_cdevs[2].led_cdev.brightness_set(&clevo_mcled_cdevs[2].led_cdev, brightness); + } +} +EXPORT_SYMBOL(clevo_leds_set_brightness_extern); + +void clevo_leds_set_color_extern(u32 color) { + if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + clevo_mcled_cdevs[0].subled_info[0].intensity = (color >> 16) & 0xff; + clevo_mcled_cdevs[0].subled_info[1].intensity = (color >> 8) & 0xff; + clevo_mcled_cdevs[0].subled_info[2].intensity = color & 0xff; + clevo_mcled_cdevs[0].led_cdev.brightness_set(&clevo_mcled_cdevs[0].led_cdev, clevo_mcled_cdevs[0].led_cdev.brightness); + } + else if (clevo_kb_backlight_type == CLEVO_KB_BACKLIGHT_TYPE_3_ZONE_RGB) { + clevo_mcled_cdevs[0].subled_info[0].intensity = (color >> 16) & 0xff; + clevo_mcled_cdevs[0].subled_info[1].intensity = (color >> 8) & 0xff; + clevo_mcled_cdevs[0].subled_info[2].intensity = color & 0xff; + clevo_mcled_cdevs[0].led_cdev.brightness_set(&clevo_mcled_cdevs[0].led_cdev, clevo_mcled_cdevs[0].led_cdev.brightness); + clevo_mcled_cdevs[1].subled_info[0].intensity = (color >> 16) & 0xff; + clevo_mcled_cdevs[1].subled_info[1].intensity = (color >> 8) & 0xff; + clevo_mcled_cdevs[1].subled_info[2].intensity = color & 0xff; + clevo_mcled_cdevs[1].led_cdev.brightness_set(&clevo_mcled_cdevs[1].led_cdev, clevo_mcled_cdevs[1].led_cdev.brightness); + clevo_mcled_cdevs[2].subled_info[0].intensity = (color >> 16) & 0xff; + clevo_mcled_cdevs[2].subled_info[1].intensity = (color >> 8) & 0xff; + clevo_mcled_cdevs[2].subled_info[2].intensity = color & 0xff; + clevo_mcled_cdevs[2].led_cdev.brightness_set(&clevo_mcled_cdevs[2].led_cdev, clevo_mcled_cdevs[2].led_cdev.brightness); + } +} +EXPORT_SYMBOL(clevo_leds_set_color_extern); + +MODULE_LICENSE("GPL"); + +#endif // CLEVO_LEDS_H diff --git a/src/clevo_wmi.c b/src/clevo_wmi.c index 14320f4..f7e0186 100644 --- a/src/clevo_wmi.c +++ b/src/clevo_wmi.c @@ -23,7 +23,7 @@ #include #include "clevo_interfaces.h" -static int clevo_wmi_evaluate(u32 wmi_method_id, u32 wmi_arg, u32 *result) +static int clevo_wmi_evaluate(u32 wmi_method_id, u32 wmi_arg, union acpi_object **result) { struct acpi_buffer acpi_buffer_in = { (acpi_size)sizeof(wmi_arg), &wmi_arg }; @@ -45,26 +45,17 @@ static int clevo_wmi_evaluate(u32 wmi_method_id, u32 wmi_arg, u32 *result) if (!acpi_result) { pr_err("failed to evaluate WMI method\n"); return_status = -1; - } else { - if (acpi_result->type == ACPI_TYPE_INTEGER) { - if (!IS_ERR_OR_NULL(result)) { - *result = (u32)acpi_result->integer.value; - /*pr_debug( - "evaluate wmi cmd: %0#4x arg: %0#10x\n", - wmi_method_id, wmi_arg);*/ - } - } else { - pr_err("unknown output from wmi method\n"); - return_status = -EIO; + } + else { + if (!IS_ERR_OR_NULL(result)) { + *result = acpi_result; } } - kfree(acpi_result); - return return_status; } -u32 clevo_wmi_interface_method_call(u8 cmd, u32 arg, u32 *result_value) +u32 clevo_wmi_interface_method_call(u8 cmd, u32 arg, union acpi_object **result_value) { return clevo_wmi_evaluate(cmd, arg, result_value); } @@ -80,7 +71,8 @@ static int clevo_wmi_probe(struct wmi_device *wdev) static int clevo_wmi_probe(struct wmi_device *wdev, const void *dummy_context) #endif { - u32 status, ret; + u32 status; + union acpi_object *out_obj; pr_debug("clevo_wmi driver probe\n"); @@ -96,16 +88,18 @@ static int clevo_wmi_probe(struct wmi_device *wdev, const void *dummy_context) // Since the WMI GUIDs aren't unique let's (at least) // check the return of some "known existing general" method - status = clevo_wmi_evaluate(0x52, 0, &ret); + status = clevo_wmi_evaluate(0x52, 0, &out_obj); if (status < 0) { pr_debug("probe: Clevo GUIDs present but method call failed\n"); return -ENODEV; } - if (ret == 0xffffffff) { + if (out_obj->type != ACPI_TYPE_INTEGER || (out_obj->type == ACPI_TYPE_INTEGER && (u32)out_obj->integer.value == 0xffffffff)) { pr_debug( "probe: Clevo GUIDs present but method returned unexpected value\n"); + ACPI_FREE(out_obj); return -ENODEV; } + ACPI_FREE(out_obj); // Add this interface clevo_keyboard_add_interface(&clevo_wmi_interface); @@ -131,7 +125,18 @@ static void clevo_wmi_remove(struct wmi_device *wdev) static void clevo_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy) { u32 event_value; - clevo_wmi_evaluate(0x01, 0, &event_value); + union acpi_object *out_obj; + u32 status; + + status = clevo_wmi_evaluate(0x01, 0, &out_obj); + if (!status) { + if (out_obj->type == ACPI_TYPE_INTEGER) { + event_value = (u32)out_obj->integer.value; + } else { + pr_err("return type not integer, use clevo_evaluate_method2\n"); + } + ACPI_FREE(out_obj); + } pr_debug("clevo_wmi notify\n"); if (!IS_ERR_OR_NULL(clevo_wmi_interface.event_callb)) { // Execute registered callback @@ -161,7 +166,7 @@ module_wmi_driver(clevo_wmi_driver); MODULE_AUTHOR("TUXEDO Computers GmbH "); MODULE_DESCRIPTION("Driver for Clevo WMI interface"); -MODULE_VERSION("0.0.4"); +MODULE_VERSION("0.1.0"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(wmi, clevo_wmi_device_ids); diff --git a/src/tuxedo_io/tuxedo_io.c b/src/tuxedo_io/tuxedo_io.c index 07e0f02..1121ebd 100644 --- a/src/tuxedo_io/tuxedo_io.c +++ b/src/tuxedo_io/tuxedo_io.c @@ -34,7 +34,7 @@ MODULE_DESCRIPTION("Hardware interface for TUXEDO laptops"); MODULE_AUTHOR("TUXEDO Computers GmbH "); -MODULE_VERSION("0.3.2"); +MODULE_VERSION("0.3.3"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CLEVO_INTERFACES(); @@ -254,7 +254,7 @@ static long clevo_ioctl_interface(struct file *file, unsigned int cmd, unsigned break; case W_CL_PERF_PROFILE: copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument)); - clevo_arg = (CLEVO_OPT_SUBCMD_SET_PERF_PROF << 0x18) | (argument & 0xff); + clevo_arg = (CLEVO_CMD_OPT_SUB_SET_PERF_PROF << 0x18) | (argument & 0xff); clevo_evaluate_method(CLEVO_CMD_OPT, clevo_arg, &result); break; } @@ -811,6 +811,8 @@ static int __init tuxedo_io_init(void) id_check_uniwill = uniwill_identify(); #ifdef DEBUG + pr_debug("DEBUG is defined\n"); + if (id_check_clevo == 0 && id_check_uniwill == 0) { pr_debug("No matching hardware found on module load\n"); } diff --git a/src/tuxedo_keyboard_common.h b/src/tuxedo_keyboard_common.h index 84f6059..aae510b 100644 --- a/src/tuxedo_keyboard_common.h +++ b/src/tuxedo_keyboard_common.h @@ -102,41 +102,4 @@ static struct color_list_t color_list = { } }; -/** - * Looks up a color in the color_list - * - * Returns found color value, or 0xffffffff if string did not match - */ -static u32 color_lookup(const struct color_list_t *color_list, const char *color_name) -{ - u32 found_color = 0xffffffff; - int i; - for (i = 0; i < color_list->size; ++i) { - if (strcmp(color_list->colors[i].name, color_name) == 0) { - found_color = color_list->colors[i].code; - } - } - - return found_color; -} - -// Common parameters - -static int brightness_validator(const char *val, - const struct kernel_param *brightness_param); -static const struct kernel_param_ops param_ops_brightness_ops = { - .set = brightness_validator, - .get = param_get_int, -}; - -static ushort param_brightness = 0xffff; // Default unset value (higher than max) -module_param_cb(brightness, ¶m_ops_brightness_ops, ¶m_brightness, - S_IRUSR); -MODULE_PARM_DESC(brightness, "Set the Keyboard Brightness"); - -#define COLOR_STRING_LEN 20 -static char param_color[COLOR_STRING_LEN]; -module_param_string(color, param_color, COLOR_STRING_LEN, S_IRUSR); -MODULE_PARM_DESC(color, "Preset color for the keyboard backlight as string"); - -#endif \ No newline at end of file +#endif diff --git a/src/uniwill_interfaces.h b/src/uniwill_interfaces.h index f8895e4..705ccda 100644 --- a/src/uniwill_interfaces.h +++ b/src/uniwill_interfaces.h @@ -35,11 +35,40 @@ #define UNIWILL_INTERFACE_WMI_STRID "uniwill_wmi" -typedef u32 (uniwill_read_ec_ram_t)(u16, u8*); -typedef u32 (uniwill_write_ec_ram_t)(u16, u8); -typedef u32 (uniwill_write_ec_ram_with_retry_t)(u16, u8, int); +typedef int (uniwill_read_ec_ram_t)(u16, u8*); +typedef int (uniwill_write_ec_ram_t)(u16, u8); +typedef int (uniwill_write_ec_ram_with_retry_t)(u16, u8, int); typedef void (uniwill_event_callb_t)(u32); +// UW_EC_REG_* known relevant EC address exposing some information or function +// UW_EC_REG_*_BIT_* single bit from byte holding information, should be handled with bit-wise operations +// UW_EC_REG_*_VALUE_* discrete value of the whole byte with special meaning +// UW_EC_REG_*_SUBCMD_* writing this discrete value triggers special behaviour + +#define UW_EC_REG_KBD_BL_STATUS 0x078c +#define UW_EC_REG_KBD_BL_STATUS_BIT_WHITE_ONLY_KB 0x01 +#define UW_EC_REG_KBD_BL_STATUS_SUBCMD_RESET 0x10 + +#define UW_EC_REG_KBD_BL_MAX_BRIGHTNESS 0x1801 +#define UW_EC_REG_KBD_BL_WHITE_BRIGHTNESS 0x1802 +#define UW_EC_REG_KBD_BL_RGB_RED_BRIGHTNESS 0x1803 +#define UW_EC_REG_KBD_BL_RGB_GREEN_BRIGHTNESS 0x1805 +#define UW_EC_REG_KBD_BL_RGB_BLUE_BRIGHTNESS 0x1808 + +#define UW_EC_REG_BAREBONE_ID 0x0740 +#define UW_EC_REG_BAREBONE_ID_VALUE_PFxxxxx 0x09 +#define UW_EC_REG_BAREBONE_ID_VALUE_PFxMxxx 0x0e +#define UW_EC_REG_BAREBONE_ID_VALUE_PH4TRX1 0x12 +#define UW_EC_REG_BAREBONE_ID_VALUE_PH4TUX1 0x13 +#define UW_EC_REG_BAREBONE_ID_VALUE_PH4TQx1 0x14 +#define UW_EC_REG_BAREBONE_ID_VALUE_PH6TRX1 0x15 +#define UW_EC_REG_BAREBONE_ID_VALUE_PH6TQxx 0x16 +#define UW_EC_REG_BAREBONE_ID_VALUE_PH4Axxx 0x17 + +#define UW_EC_REG_FEATURES_0 0x0765 +#define UW_EC_REG_FEATURES_1 0x0766 +#define UW_EC_REG_FEATURES_1_BIT_1_ZONE_RGB_KB 0x04 + struct uniwill_interface_t { char *string_id; uniwill_event_callb_t *event_callb; @@ -47,6 +76,13 @@ struct uniwill_interface_t { uniwill_write_ec_ram_t *write_ec_ram; }; +int uniwill_add_interface(struct uniwill_interface_t *new_interface); +int uniwill_remove_interface(struct uniwill_interface_t *interface); +uniwill_read_ec_ram_t uniwill_read_ec_ram; +uniwill_write_ec_ram_t uniwill_write_ec_ram; +uniwill_write_ec_ram_with_retry_t uniwill_write_ec_ram_with_retry; +int uniwill_get_active_interface_id(char **id_str); + #define UW_MODEL_PF5LUXG 0x09 #define UW_MODEL_PH4TUX 0x13 #define UW_MODEL_PH4TRX 0x12 @@ -72,12 +108,6 @@ struct uniwill_device_features_t { bool uniwill_has_charging_profile; }; -u32 uniwill_add_interface(struct uniwill_interface_t *new_interface); -u32 uniwill_remove_interface(struct uniwill_interface_t *interface); -uniwill_read_ec_ram_t uniwill_read_ec_ram; -uniwill_write_ec_ram_t uniwill_write_ec_ram; -uniwill_write_ec_ram_with_retry_t uniwill_write_ec_ram_with_retry; -u32 uniwill_get_active_interface_id(char **id_str); struct uniwill_device_features_t *uniwill_get_device_features(void); union uw_ec_read_return { diff --git a/src/uniwill_keyboard.h b/src/uniwill_keyboard.h index 2193516..e72d4ee 100644 --- a/src/uniwill_keyboard.h +++ b/src/uniwill_keyboard.h @@ -16,6 +16,9 @@ * You should have received a copy of the GNU General Public License * along with this software. If not, see . */ +#ifndef UNIWILL_KEYBOARD_H +#define UNIWILL_KEYBOARD_H + #include "tuxedo_keyboard_common.h" #include #include @@ -24,17 +27,11 @@ #include #include #include +#include #include #include #include "uniwill_interfaces.h" - -#define UNIWILL_WMI_MGMT_GUID_BA "ABBC0F6D-8EA1-11D1-00A0-C90629100000" -#define UNIWILL_WMI_MGMT_GUID_BB "ABBC0F6E-8EA1-11D1-00A0-C90629100000" -#define UNIWILL_WMI_MGMT_GUID_BC "ABBC0F6F-8EA1-11D1-00A0-C90629100000" - -#define UNIWILL_WMI_EVENT_GUID_0 "ABBC0F70-8EA1-11D1-00A0-C90629100000" -#define UNIWILL_WMI_EVENT_GUID_1 "ABBC0F71-8EA1-11D1-00A0-C90629100000" -#define UNIWILL_WMI_EVENT_GUID_2 "ABBC0F72-8EA1-11D1-00A0-C90629100000" +#include "uniwill_leds.h" #define UNIWILL_OSD_RADIOON 0x01A #define UNIWILL_OSD_RADIOOFF 0x01B @@ -44,6 +41,7 @@ #define UNIWILL_OSD_KB_LED_LEVEL3 0x03E #define UNIWILL_OSD_KB_LED_LEVEL4 0x03F #define UNIWILL_OSD_DC_ADAPTER_CHANGE 0x0AB +#define UNIWILL_OSD_MODE_CHANGE_KEY_EVENT 0x0B0 #define UNIWILL_KEY_RFKILL 0x0A4 #define UNIWILL_KEY_KBDILLUMDOWN 0x0B1 @@ -52,30 +50,16 @@ #define UNIWILL_OSD_TOUCHPADWORKAROUND 0xFFF -#define UNIWILL_BRIGHTNESS_MIN 0x00 -#define UNIWILL_BRIGHTNESS_MAX 0xc8 -#define UNIWILL_BRIGHTNESS_DEFAULT UNIWILL_BRIGHTNESS_MAX * 0.30 -#define UNIWILL_COLOR_DEFAULT 0xffffff - static void uw_charging_priority_write_state(void); static void uw_charging_profile_write_state(void); struct tuxedo_keyboard_driver uniwill_keyboard_driver; -struct kbd_led_state_uw_t { - u32 brightness; - u32 color; -} kbd_led_state_uw = { - .brightness = UNIWILL_BRIGHTNESS_DEFAULT, - .color = UNIWILL_COLOR_DEFAULT, -}; - struct uniwill_device_features_t uniwill_device_features; static bool uw_feats_loaded = false; static u8 uniwill_kbd_bl_enable_state_on_start; -static bool uniwill_kbd_bl_type_rgb_single_color = true; static struct key_entry uniwill_wmi_keymap[] = { // { KE_KEY, UNIWILL_OSD_RADIOON, { KEY_RFKILL } }, @@ -88,6 +72,11 @@ static struct key_entry uniwill_wmi_keymap[] = { { KE_KEY, UNIWILL_KEY_KBDILLUMDOWN, { KEY_KBDILLUMDOWN } }, { KE_KEY, UNIWILL_KEY_KBDILLUMUP, { KEY_KBDILLUMUP } }, { KE_KEY, UNIWILL_KEY_KBDILLUMTOGGLE, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL0, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL1, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL2, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL3, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, UNIWILL_OSD_KB_LED_LEVEL4, { KEY_KBDILLUMTOGGLE } }, // Only used to put ev bits { KE_KEY, 0xffff, { KEY_F6 } }, { KE_KEY, 0xffff, { KEY_LEFTALT } }, @@ -101,9 +90,9 @@ static struct uniwill_interfaces_t { uniwill_event_callb_t uniwill_event_callb; -u32 uniwill_read_ec_ram(u16 address, u8 *data) +int uniwill_read_ec_ram(u16 address, u8 *data) { - u32 status; + int status; if (!IS_ERR_OR_NULL(uniwill_interfaces.wmi)) status = uniwill_interfaces.wmi->read_ec_ram(address, data); @@ -116,9 +105,9 @@ u32 uniwill_read_ec_ram(u16 address, u8 *data) } EXPORT_SYMBOL(uniwill_read_ec_ram); -u32 uniwill_write_ec_ram(u16 address, u8 data) +int uniwill_write_ec_ram(u16 address, u8 data) { - u32 status; + int status; if (!IS_ERR_OR_NULL(uniwill_interfaces.wmi)) status = uniwill_interfaces.wmi->write_ec_ram(address, data); @@ -131,7 +120,7 @@ u32 uniwill_write_ec_ram(u16 address, u8 data) } EXPORT_SYMBOL(uniwill_write_ec_ram); -u32 uniwill_write_ec_ram_with_retry(u16 address, u8 data, int retries) +int uniwill_write_ec_ram_with_retry(u16 address, u8 data, int retries) { u32 status; int i; @@ -159,7 +148,7 @@ EXPORT_SYMBOL(uniwill_write_ec_ram_with_retry); static DEFINE_MUTEX(uniwill_interface_modification_lock); -u32 uniwill_add_interface(struct uniwill_interface_t *interface) +int uniwill_add_interface(struct uniwill_interface_t *interface) { mutex_lock(&uniwill_interface_modification_lock); @@ -181,7 +170,7 @@ u32 uniwill_add_interface(struct uniwill_interface_t *interface) } EXPORT_SYMBOL(uniwill_add_interface); -u32 uniwill_remove_interface(struct uniwill_interface_t *interface) +int uniwill_remove_interface(struct uniwill_interface_t *interface) { mutex_lock(&uniwill_interface_modification_lock); @@ -201,7 +190,7 @@ u32 uniwill_remove_interface(struct uniwill_interface_t *interface) } EXPORT_SYMBOL(uniwill_remove_interface); -u32 uniwill_get_active_interface_id(char **id_str) +int uniwill_get_active_interface_id(char **id_str) { if (IS_ERR_OR_NULL(uniwill_interfaces.wmi)) return -ENODEV; @@ -258,89 +247,15 @@ static struct notifier_block keyboard_notifier_block = { .notifier_call = keyboard_notifier_callb }; -static u8 uniwill_read_kbd_bl_enabled(void) -{ - u8 backlight_data; - u8 enabled = 0xff; - - uniwill_read_ec_ram(0x078c, &backlight_data); - enabled = (backlight_data >> 1) & 0x01; - enabled = !enabled; - - return enabled; -} - static void uniwill_write_kbd_bl_enable(u8 enable) { u8 backlight_data; enable = enable & 0x01; - uniwill_read_ec_ram(0x078c, &backlight_data); + uniwill_read_ec_ram(UW_EC_REG_KBD_BL_STATUS, &backlight_data); backlight_data = backlight_data & ~(1 << 1); backlight_data |= (!enable << 1); - uniwill_write_ec_ram(0x078c, backlight_data); -} - -/*static u32 uniwill_read_kbd_bl_br_state(u8 *brightness_state) -{ - u8 backlight_data; - u32 result; - - uniwill_read_ec_ram(0x078c, &backlight_data); - *brightness_state = (backlight_data & 0xf0) >> 4; - result = 0; - - return result; -}*/ - -static u32 uniwill_read_kbd_bl_rgb(u8 *red, u8 *green, u8 *blue) -{ - u32 result; - - uniwill_read_ec_ram(0x1803, red); - uniwill_read_ec_ram(0x1805, green); - uniwill_read_ec_ram(0x1808, blue); - - result = 0; - - return result; -} - -static void uniwill_write_kbd_bl_rgb(u8 red, u8 green, u8 blue) -{ - if (red > 0xc8) red = 0xc8; - if (green > 0xc8) green = 0xc8; - if (blue > 0xc8) blue = 0xc8; - uniwill_write_ec_ram(0x1803, red); - uniwill_write_ec_ram(0x1805, green); - uniwill_write_ec_ram(0x1808, blue); - TUXEDO_DEBUG("Wrote kbd color [%0#4x, %0#4x, %0#4x]\n", red, green, blue); -} - -static void uniwill_write_kbd_bl_state(void) { - // Get single colors from state - u32 color_red = ((kbd_led_state_uw.color >> 0x10) & 0xff); - u32 color_green = (kbd_led_state_uw.color >> 0x08) & 0xff; - u32 color_blue = (kbd_led_state_uw.color >> 0x00) & 0xff; - - u32 brightness_percentage = (kbd_led_state_uw.brightness * 100) / UNIWILL_BRIGHTNESS_MAX; - - // Scale color values to valid range - color_red = (color_red * 0xc8) / 0xff; - color_green = (color_green * 0xc8) / 0xff; - color_blue = (color_blue * 0xc8) / 0xff; - - // Scale the respective color values with brightness - color_red = (color_red * brightness_percentage) / 100; - color_green = (color_green * brightness_percentage) / 100; - color_blue = (color_blue * brightness_percentage) / 100; - - uniwill_write_kbd_bl_rgb(color_red, color_green, color_blue); -} - -static void uniwill_write_kbd_bl_reset(void) -{ - uniwill_write_ec_ram(0x078c, 0x10); + uniwill_write_ec_ram(UW_EC_REG_KBD_BL_STATUS, backlight_data); } void uniwill_event_callb(u32 code) @@ -351,7 +266,7 @@ void uniwill_event_callb(u32 code) } // Special key combination when mode change key is pressed - if (code == 0xb0) { + if (code == UNIWILL_OSD_MODE_CHANGE_KEY_EVENT) { input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTMETA, 1); input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 1); input_report_key(uniwill_keyboard_driver.input_device, KEY_F6, 1); @@ -362,131 +277,18 @@ void uniwill_event_callb(u32 code) input_sync(uniwill_keyboard_driver.input_device); } - // Keyboard backlight brightness toggle - if (uniwill_kbd_bl_type_rgb_single_color) { - switch (code) { - case UNIWILL_OSD_KB_LED_LEVEL0: - kbd_led_state_uw.brightness = 0x00; - uniwill_write_kbd_bl_state(); - break; - case UNIWILL_OSD_KB_LED_LEVEL1: - kbd_led_state_uw.brightness = 0x20; - uniwill_write_kbd_bl_state(); - break; - case UNIWILL_OSD_KB_LED_LEVEL2: - kbd_led_state_uw.brightness = 0x50; - uniwill_write_kbd_bl_state(); - break; - case UNIWILL_OSD_KB_LED_LEVEL3: - kbd_led_state_uw.brightness = 0x80; - uniwill_write_kbd_bl_state(); - break; - case UNIWILL_OSD_KB_LED_LEVEL4: - kbd_led_state_uw.brightness = 0xc8; - uniwill_write_kbd_bl_state(); - break; - // Refresh keyboard state on cable switch event - case UNIWILL_OSD_DC_ADAPTER_CHANGE: - uniwill_write_kbd_bl_state(); - break; - } - } + // Refresh keyboard state and charging prio on cable switch event + if (code == UNIWILL_OSD_DC_ADAPTER_CHANGE) { + uniwill_leds_restore_state_extern(); - switch (code) { - case UNIWILL_OSD_DC_ADAPTER_CHANGE: msleep(50); uw_charging_priority_write_state(); - break; } } -static ssize_t uw_brightness_show(struct device *child, - struct device_attribute *attr, char *buffer) +static void uw_kbd_bl_init_set(struct platform_device *dev) { - return sprintf(buffer, "%d\n", kbd_led_state_uw.brightness); -} - -static ssize_t uw_brightness_store(struct device *child, - struct device_attribute *attr, - const char *buffer, size_t size) -{ - u32 brightness_input; - int err = kstrtouint(buffer, 0, &brightness_input); - if (err) return err; - if (brightness_input > UNIWILL_BRIGHTNESS_MAX) return -EINVAL; - kbd_led_state_uw.brightness = (u8)brightness_input; - uniwill_write_kbd_bl_state(); - return size; -} - -static ssize_t uw_color_string_show(struct device *child, - struct device_attribute *attr, char *buffer) -{ - int i; - sprintf(buffer, "Color values:"); - for (i = 0; i < color_list.size; ++i) { - sprintf(buffer + strlen(buffer), " %s", - color_list.colors[i].name); - } - sprintf(buffer + strlen(buffer), "\n"); - return strlen(buffer); -} - -static ssize_t uw_color_string_store(struct device *child, - struct device_attribute *attr, - const char *buffer, size_t size) -{ - u32 color_value; - char *buffer_copy; - - buffer_copy = kmalloc(size + 1, GFP_KERNEL); - strcpy(buffer_copy, buffer); - color_value = color_lookup(&color_list, strstrip(buffer_copy)); - kfree(buffer_copy); - - if (color_value > 0xffffff) return -EINVAL; - kbd_led_state_uw.color = color_value; - uniwill_write_kbd_bl_state(); - return size; -} - -// Device attributes used by uw kbd -struct uw_kbd_dev_attrs_t { - struct device_attribute brightness; - struct device_attribute color_string; -} uw_kbd_dev_attrs = { - .brightness = __ATTR(brightness, 0644, uw_brightness_show, uw_brightness_store), - .color_string = __ATTR(color_string, 0644, uw_color_string_show, uw_color_string_store) -}; - -// Device attributes used for uw_kbd_bl_color -static struct attribute *uw_kbd_bl_color_attrs[] = { - &uw_kbd_dev_attrs.brightness.attr, - &uw_kbd_dev_attrs.color_string.attr, - NULL -}; - -static struct attribute_group uw_kbd_bl_color_attr_group = { - .name = "uw_kbd_bl_color", - .attrs = uw_kbd_bl_color_attrs -}; - -static void uw_kbd_bl_init_set(void) -{ - if (uniwill_kbd_bl_type_rgb_single_color) { - // Reset keyboard backlight - uniwill_write_kbd_bl_reset(); - // Make sure reset finish before continue - msleep(100); - - // Disable backlight while initializing - // uniwill_write_kbd_bl_enable(0); - - // Update keyboard backlight according to the current state - uniwill_write_kbd_bl_state(); - } - - // Enable keyboard backlight + uniwill_leds_init_late(dev); uniwill_write_kbd_bl_enable(1); } @@ -500,6 +302,28 @@ static struct timer_list uw_kbd_bl_init_timer; static volatile int uw_kbd_bl_check_count = 40; static int uw_kbd_bl_init_check_interval_ms = 500; +static int uniwill_read_kbd_bl_rgb(u8 *red, u8 *green, u8 *blue) +{ + int result = 0; + + result = uniwill_read_ec_ram(UW_EC_REG_KBD_BL_RGB_RED_BRIGHTNESS, red); + if (result) { + return result; + } + result = uniwill_read_ec_ram(UW_EC_REG_KBD_BL_RGB_GREEN_BRIGHTNESS, green); + if (result) { + return result; + } + result = uniwill_read_ec_ram(UW_EC_REG_KBD_BL_RGB_BLUE_BRIGHTNESS, blue); + if (result) { + return result; + } + + return result; +} + +static struct platform_device *uw_kbd_bl_init_ready_check_work_func_args_dev; + static void uw_kbd_bl_init_ready_check_work_func(struct work_struct *work) { u8 uw_cur_red, uw_cur_green, uw_cur_blue; @@ -515,7 +339,7 @@ static void uw_kbd_bl_init_ready_check_work_func(struct work_struct *work) } if (prev_colors_same) { - uw_kbd_bl_init_set(); + uw_kbd_bl_init_set(uw_kbd_bl_init_ready_check_work_func_args_dev); del_timer(&uw_kbd_bl_init_timer); } else { if (uw_kbd_bl_check_count != 0) { @@ -540,51 +364,15 @@ static int uw_kbd_bl_init(struct platform_device *dev) { int status = 0; - uniwill_kbd_bl_type_rgb_single_color = false - // New names - | dmi_match(DMI_BOARD_NAME, "POLARIS1501A1650TI") - | dmi_match(DMI_BOARD_NAME, "POLARIS1501A2060") - | dmi_match(DMI_BOARD_NAME, "POLARIS1501I1650TI") - | dmi_match(DMI_BOARD_NAME, "POLARIS1501I2060") - | dmi_match(DMI_BOARD_NAME, "POLARIS1701A1650TI") - | dmi_match(DMI_BOARD_NAME, "POLARIS1701A2060") - | dmi_match(DMI_BOARD_NAME, "POLARIS1701I1650TI") - | dmi_match(DMI_BOARD_NAME, "POLARIS1701I2060") -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) - | dmi_match(DMI_PRODUCT_SKU, "POLARIS1XA02") - | dmi_match(DMI_PRODUCT_SKU, "POLARIS1XI02") - | dmi_match(DMI_PRODUCT_SKU, "POLARIS1XA03") - | dmi_match(DMI_PRODUCT_SKU, "POLARIS1XI03") -#endif - - // Old names - // | dmi_match(DMI_BOARD_NAME, "Polaris15I01") - // | dmi_match(DMI_BOARD_NAME, "Polaris17I01") - // | dmi_match(DMI_BOARD_NAME, "Polaris15A01") - // | dmi_match(DMI_BOARD_NAME, "Polaris1501I2060") - // | dmi_match(DMI_BOARD_NAME, "Polaris1701I2060") - ; - #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0) - TUXEDO_ERROR( - "Warning: Kernel version less that 4.18, keyboard backlight might not be properly recognized."); + TUXEDO_ERROR("Warning: Kernel version less that 4.18, keyboard backlight might not be properly recognized."); #endif - // Save previous enable state - uniwill_kbd_bl_enable_state_on_start = uniwill_read_kbd_bl_enabled(); - - if (uniwill_kbd_bl_type_rgb_single_color) { - // Initialize keyboard backlight driver state according to parameters - if (param_brightness > UNIWILL_BRIGHTNESS_MAX) param_brightness = UNIWILL_BRIGHTNESS_DEFAULT; - kbd_led_state_uw.brightness = param_brightness; - if (color_lookup(&color_list, param_color) <= (u32) 0xffffff) kbd_led_state_uw.color = color_lookup(&color_list, param_color); - else kbd_led_state_uw.color = UNIWILL_COLOR_DEFAULT; - - // Init sysfs bl attributes group - status = sysfs_create_group(&dev->dev.kobj, &uw_kbd_bl_color_attr_group); - if (status) TUXEDO_ERROR("Failed to create sysfs group\n"); + uniwill_leds_init_early(dev); + if (uniwill_leds_get_backlight_type() == UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { // Start periodic checking of animation, set and enable bl when done + uw_kbd_bl_init_ready_check_work_func_args_dev = dev; timer_setup(&uw_kbd_bl_init_timer, uw_kbd_bl_init_ready_check, 0); mod_timer(&uw_kbd_bl_init_timer, jiffies + msecs_to_jiffies(uw_kbd_bl_init_check_interval_ms)); } else { @@ -1291,9 +1079,7 @@ static int uniwill_keyboard_remove(struct platform_device *dev) if (uw_charging_profile_loaded) sysfs_remove_group(&dev->dev.kobj, &uw_charging_profile_attr_group); - if (uniwill_kbd_bl_type_rgb_single_color) { - sysfs_remove_group(&dev->dev.kobj, &uw_kbd_bl_color_attr_group); - } + uniwill_leds_remove(dev); // Restore previous backlight enable state if (uniwill_kbd_bl_enable_state_on_start != 0xff) { @@ -1321,11 +1107,7 @@ static int uniwill_keyboard_suspend(struct platform_device *dev, pm_message_t st static int uniwill_keyboard_resume(struct platform_device *dev) { - if (uniwill_kbd_bl_type_rgb_single_color) { - uniwill_write_kbd_bl_reset(); - msleep(100); - uniwill_write_kbd_bl_state(); - } + uniwill_leds_restore_state_extern(); uniwill_write_kbd_bl_enable(1); return 0; } @@ -1346,3 +1128,5 @@ struct tuxedo_keyboard_driver uniwill_keyboard_driver = { .probe = uniwill_keyboard_probe, .key_map = uniwill_wmi_keymap, }; + +#endif // UNIWILL_KEYBOARD_H diff --git a/src/uniwill_leds.h b/src/uniwill_leds.h new file mode 100644 index 0000000..63639fa --- /dev/null +++ b/src/uniwill_leds.h @@ -0,0 +1,329 @@ +/*! + * Copyright (c) 2018-2020 TUXEDO Computers GmbH + * + * This file is part of tuxedo-keyboard. + * + * tuxedo-keyboard is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. If not, see . + */ + +#ifndef UNIWILL_LEDS_H +#define UNIWILL_LEDS_H + +#include +#include +#include +#include + +enum uniwill_kb_backlight_types { + UNIWILL_KB_BACKLIGHT_TYPE_NONE, + UNIWILL_KB_BACKLIGHT_TYPE_FIXED_COLOR, + UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB, + UNIWILL_KB_BACKLIGHT_TYPE_PER_KEY_RGB +}; + +#define UNIWILL_KBD_BRIGHTNESS_MAX 0xff +#define UNIWILL_KBD_BRIGHTNESS_DEFAULT 0x00 + +#define UNIWILL_KBD_BRIGHTNESS_WHITE_MAX 0x02 +#define UNIWILL_KBD_BRIGHTNESS_WHITE_DEFAULT 0x00 + +#define UNIWILL_KB_COLOR_DEFAULT_RED 0xff +#define UNIWILL_KB_COLOR_DEFAULT_GREEN 0xff +#define UNIWILL_KB_COLOR_DEFAULT_BLUE 0xff +#define UNIWILL_KB_COLOR_DEFAULT ((UNIWILL_KB_COLOR_DEFAULT_RED << 16) + (UNIWILL_KB_COLOR_DEFAULT_GREEN << 8) + UNIWILL_KB_COLOR_DEFAULT_BLUE) + +int uniwill_leds_init_early(struct platform_device *dev); +int uniwill_leds_init_late(struct platform_device *dev); +int uniwill_leds_remove(struct platform_device *dev); +enum uniwill_kb_backlight_types uniwill_leds_get_backlight_type(void); +void uniwill_leds_restore_state_extern(void); +void uniwill_leds_set_brightness_extern(enum led_brightness brightness); +void uniwill_leds_set_color_extern(u32 color); + +// TODO The following should go into a seperate .c file, but for this to work more reworking is required in the tuxedo_keyboard structure. + +#include "uniwill_leds.h" + +#include "uniwill_interfaces.h" + +#include + +static enum uniwill_kb_backlight_types uniwill_kb_backlight_type = UNIWILL_KB_BACKLIGHT_TYPE_NONE; +static bool uw_leds_initialized = false; + +static int uniwill_write_kbd_bl_white(u8 brightness) +{ + u8 data; + + uniwill_read_ec_ram(UW_EC_REG_KBD_BL_RGB_BLUE_BRIGHTNESS, &data); + // When keyboard backlight is off, new settings to 0x078c do not get applied automatically + // on Pulse Gen1/2 until next keypress or manual change to 0x1808 (immediate brightness + // value for some reason. + // Sidenote: IBP Gen6/7 has immediate brightness value on 0x1802 and not on 0x1808, but does + // not need this workaround. + if (!data) { + uniwill_write_ec_ram(UW_EC_REG_KBD_BL_RGB_BLUE_BRIGHTNESS, 0x01); + } + + data = 0; + uniwill_read_ec_ram(UW_EC_REG_KBD_BL_STATUS, &data); + data &= 0x0f; // lower bits must be preserved + data |= UW_EC_REG_KBD_BL_STATUS_SUBCMD_RESET; + data |= brightness << 5; + return uniwill_write_ec_ram(UW_EC_REG_KBD_BL_STATUS, data); +} + +static int uniwill_write_kbd_bl_rgb(u8 red, u8 green, u8 blue) +{ + int result = 0; + + result = uniwill_write_ec_ram(UW_EC_REG_KBD_BL_RGB_RED_BRIGHTNESS, red); + if (result) { + return result; + } + result = uniwill_write_ec_ram(UW_EC_REG_KBD_BL_RGB_GREEN_BRIGHTNESS, green); + if (result) { + return result; + } + result = uniwill_write_ec_ram(UW_EC_REG_KBD_BL_RGB_BLUE_BRIGHTNESS, blue); + if (result) { + return result; + } + + pr_debug("Wrote kbd color [%0#4x, %0#4x, %0#4x]\n", red, green, blue); + + return result; +} + +static void uniwill_leds_set_brightness(struct led_classdev *led_cdev __always_unused, enum led_brightness brightness) { + int ret = uniwill_write_kbd_bl_white(brightness); + if (ret) { + pr_debug("uniwill_leds_set_brightness(): uniwill_write_kbd_bl_white() failed\n"); + return; + } + led_cdev->brightness = brightness; +} + +static void uniwill_leds_set_brightness_mc(struct led_classdev *led_cdev, enum led_brightness brightness) { + int ret; + struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev); + + led_mc_calc_color_components(mcled_cdev, brightness); + + ret = uniwill_write_kbd_bl_rgb(mcled_cdev->subled_info[0].brightness, + mcled_cdev->subled_info[1].brightness, + mcled_cdev->subled_info[2].brightness); + if (ret) { + pr_debug("uniwill_leds_set_brightness_mc(): uniwill_write_kbd_bl_rgb() failed\n"); + return; + } + led_cdev->brightness = brightness; +} + +static struct led_classdev uniwill_led_cdev = { + .name = "white:" LED_FUNCTION_KBD_BACKLIGHT, + .max_brightness = UNIWILL_KBD_BRIGHTNESS_WHITE_MAX, + .brightness_set = &uniwill_leds_set_brightness, + .brightness = UNIWILL_KBD_BRIGHTNESS_WHITE_DEFAULT +}; + +static struct mc_subled uw_mcled_cdev_subleds[3] = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = UNIWILL_KBD_BRIGHTNESS_MAX, + .intensity = UNIWILL_KB_COLOR_DEFAULT_RED, + .channel = 0 + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = UNIWILL_KBD_BRIGHTNESS_MAX, + .intensity = UNIWILL_KB_COLOR_DEFAULT_GREEN, + .channel = 0 + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = UNIWILL_KBD_BRIGHTNESS_MAX, + .intensity = UNIWILL_KB_COLOR_DEFAULT_BLUE, + .channel = 0 + } +}; + +static struct led_classdev_mc uniwill_mcled_cdev = { + .led_cdev.name = "rgb:" LED_FUNCTION_KBD_BACKLIGHT, + .led_cdev.max_brightness = UNIWILL_KBD_BRIGHTNESS_MAX, + .led_cdev.brightness_set = &uniwill_leds_set_brightness_mc, + .led_cdev.brightness = UNIWILL_KBD_BRIGHTNESS_DEFAULT, + .num_colors = 3, + .subled_info = uw_mcled_cdev_subleds +}; + +int uniwill_leds_init_early(struct platform_device *dev) +{ + // FIXME Use mutexes + int ret; + u8 data; + + ret = uniwill_read_ec_ram(UW_EC_REG_BAREBONE_ID, &data); + if (ret) { + pr_err("Reading barebone ID failed.\n"); + return ret; + } + + if (data == UW_EC_REG_BAREBONE_ID_VALUE_PFxxxxx || + data == UW_EC_REG_BAREBONE_ID_VALUE_PFxMxxx || + data == UW_EC_REG_BAREBONE_ID_VALUE_PH4TRX1 || + data == UW_EC_REG_BAREBONE_ID_VALUE_PH4TUX1 || + data == UW_EC_REG_BAREBONE_ID_VALUE_PH4TQx1 || + data == UW_EC_REG_BAREBONE_ID_VALUE_PH6TRX1 || + data == UW_EC_REG_BAREBONE_ID_VALUE_PH6TQxx || + data == UW_EC_REG_BAREBONE_ID_VALUE_PH4Axxx) { + ret = uniwill_read_ec_ram(UW_EC_REG_KBD_BL_STATUS, &data); + if (ret) { + pr_err("Reading keyboard backlight status failed.\n"); + return ret; + } + if (data & UW_EC_REG_KBD_BL_STATUS_BIT_WHITE_ONLY_KB) { + uniwill_kb_backlight_type = UNIWILL_KB_BACKLIGHT_TYPE_FIXED_COLOR; + } + } + else { + ret = uniwill_read_ec_ram(UW_EC_REG_FEATURES_1, &data); + if (ret) { + pr_err("Reading features 1 failed.\n"); + return ret; + } + if (data & UW_EC_REG_FEATURES_1_BIT_1_ZONE_RGB_KB) { + uniwill_kb_backlight_type = UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB; + } + } + pr_debug("Keyboard backlight type: 0x%02x\n", uniwill_kb_backlight_type); + + if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_FIXED_COLOR) { + pr_debug("Registering fixed color leds interface\n"); + ret = led_classdev_register(&dev->dev, &uniwill_led_cdev); + if (ret) { + pr_err("Registering fixed color leds interface failed\n"); + return ret; + } + } + else if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + pr_debug("Registering single zone rgb leds interface\n"); + ret = devm_led_classdev_multicolor_register(&dev->dev, &uniwill_mcled_cdev); + if (ret) { + pr_err("Registering single zone rgb leds interface failed\n"); + return ret; + } + } + + uw_leds_initialized = true; + return 0; +} +EXPORT_SYMBOL(uniwill_leds_init_early); + +int uniwill_leds_init_late(struct platform_device *dev) +{ + // FIXME Use mutexes + int ret; + + ret = uniwill_write_ec_ram(UW_EC_REG_KBD_BL_MAX_BRIGHTNESS, 0xff); + if (ret) { + pr_err("Setting max keyboard brightness value failed\n"); + uniwill_leds_remove(dev); + return ret; + } + + uniwill_leds_restore_state_extern(); + + return 0; +} +EXPORT_SYMBOL(uniwill_leds_init_late); + +int uniwill_leds_remove(struct platform_device *dev) +{ + // FIXME Use mutexes + int ret; + + if (uw_leds_initialized) { + uw_leds_initialized = false; + + uniwill_leds_set_brightness_extern(0x00); + ret = uniwill_write_ec_ram(UW_EC_REG_KBD_BL_MAX_BRIGHTNESS, 0xc8); + if (ret) { + pr_err("Resetting max keyboard brightness value failed\n"); + } + + if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_FIXED_COLOR) { + led_classdev_unregister(&uniwill_led_cdev); + } + else if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + devm_led_classdev_multicolor_unregister(&dev->dev, &uniwill_mcled_cdev); + } + } + + return ret; +} +EXPORT_SYMBOL(uniwill_leds_remove); + +enum uniwill_kb_backlight_types uniwill_leds_get_backlight_type() { + return uniwill_kb_backlight_type; +} +EXPORT_SYMBOL(uniwill_leds_get_backlight_type); + +void uniwill_leds_restore_state_extern(void) { + u8 data; + + if (uw_leds_initialized) { + if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_FIXED_COLOR) { + uniwill_led_cdev.brightness_set(&uniwill_led_cdev, uniwill_led_cdev.brightness); + } + else if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + // reset + uniwill_read_ec_ram(UW_EC_REG_KBD_BL_STATUS, &data); + data |= UW_EC_REG_KBD_BL_STATUS_SUBCMD_RESET; + uniwill_write_ec_ram(UW_EC_REG_KBD_BL_STATUS, data); + + // write + uniwill_mcled_cdev.led_cdev.brightness_set(&uniwill_mcled_cdev.led_cdev, uniwill_mcled_cdev.led_cdev.brightness); + } + } +} +EXPORT_SYMBOL(uniwill_leds_restore_state_extern); + +void uniwill_leds_set_brightness_extern(enum led_brightness brightness) { + if (uw_leds_initialized) { + if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_FIXED_COLOR) { + uniwill_led_cdev.brightness_set(&uniwill_led_cdev, brightness); + } + else if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + uniwill_mcled_cdev.led_cdev.brightness_set(&uniwill_mcled_cdev.led_cdev, brightness); + } + } +} +EXPORT_SYMBOL(uniwill_leds_set_brightness_extern); + +void uniwill_leds_set_color_extern(u32 color) { + if (uw_leds_initialized) { + if (uniwill_kb_backlight_type == UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB) { + uniwill_mcled_cdev.subled_info[0].intensity = (color >> 16) & 0xff; + uniwill_mcled_cdev.subled_info[1].intensity = (color >> 8) & 0xff; + uniwill_mcled_cdev.subled_info[2].intensity = color & 0xff; + uniwill_mcled_cdev.led_cdev.brightness_set(&uniwill_mcled_cdev.led_cdev, uniwill_mcled_cdev.led_cdev.brightness); + } + } +} +EXPORT_SYMBOL(uniwill_leds_set_color_extern); + +MODULE_LICENSE("GPL"); + +#endif // UNIWILL_LEDS_H diff --git a/src/uniwill_wmi.c b/src/uniwill_wmi.c index 7ad765e..e75b7cd 100644 --- a/src/uniwill_wmi.c +++ b/src/uniwill_wmi.c @@ -43,11 +43,11 @@ static bool uniwill_ec_direct = true; DEFINE_MUTEX(uniwill_ec_lock); -static u32 uw_wmi_ec_evaluate(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, u8 read_flag, u32 *return_buffer) +static int uw_wmi_ec_evaluate(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, u8 read_flag, u32 *return_buffer) { acpi_status status; union acpi_object *out_acpi; - u32 e_result = 0; + int e_result = 0; // Kernel buffer for input argument u32 *wmi_arg = (u32 *) kmalloc(sizeof(u32)*10, GFP_KERNEL); @@ -98,10 +98,10 @@ static u32 uw_wmi_ec_evaluate(u8 addr_low, u8 addr_high, u8 data_low, u8 data_hi /** * EC address read through WMI */ -static u32 uw_ec_read_addr_wmi(u8 addr_low, u8 addr_high, union uw_ec_read_return *output) +static int uw_ec_read_addr_wmi(u8 addr_low, u8 addr_high, union uw_ec_read_return *output) { u32 uw_data[10]; - u32 ret = uw_wmi_ec_evaluate(addr_low, addr_high, 0x00, 0x00, 1, uw_data); + int ret = uw_wmi_ec_evaluate(addr_low, addr_high, 0x00, 0x00, 1, uw_data); output->dword = uw_data[0]; // pr_debug("addr: 0x%02x%02x value: %0#4x (high: %0#4x) result: %d\n", addr_high, addr_low, output->bytes.data_low, output->bytes.data_high, ret); return ret; @@ -110,10 +110,10 @@ static u32 uw_ec_read_addr_wmi(u8 addr_low, u8 addr_high, union uw_ec_read_retur /** * EC address write through WMI */ -static u32 uw_ec_write_addr_wmi(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output) +static int uw_ec_write_addr_wmi(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output) { u32 uw_data[10]; - u32 ret = uw_wmi_ec_evaluate(addr_low, addr_high, data_low, data_high, 0, uw_data); + int ret = uw_wmi_ec_evaluate(addr_low, addr_high, data_low, data_high, 0, uw_data); output->dword = uw_data[0]; return ret; } @@ -121,9 +121,9 @@ static u32 uw_ec_write_addr_wmi(u8 addr_low, u8 addr_high, u8 data_low, u8 data_ /** * Direct EC address read */ -static u32 uw_ec_read_addr_direct(u8 addr_low, u8 addr_high, union uw_ec_read_return *output) +static int uw_ec_read_addr_direct(u8 addr_low, u8 addr_high, union uw_ec_read_return *output) { - u32 result; + int result; u8 tmp, count, flags; bool ready; bool bflag = false; @@ -180,9 +180,9 @@ static u32 uw_ec_read_addr_direct(u8 addr_low, u8 addr_high, union uw_ec_read_re return result; } -static u32 uw_ec_write_addr_direct(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output) +static int uw_ec_write_addr_direct(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output) { - u32 result = 0; + int result = 0; u8 tmp, count, flags; bool ready; bool bflag = false; @@ -239,9 +239,9 @@ static u32 uw_ec_write_addr_direct(u8 addr_low, u8 addr_high, u8 data_low, u8 da return result; } -u32 uw_wmi_read_ec_ram(u16 addr, u8 *data) +int uw_wmi_read_ec_ram(u16 addr, u8 *data) { - u32 result; + int result; u8 addr_low, addr_high; union uw_ec_read_return output; @@ -261,9 +261,9 @@ u32 uw_wmi_read_ec_ram(u16 addr, u8 *data) return result; } -u32 uw_wmi_write_ec_ram(u16 addr, u8 data) +int uw_wmi_write_ec_ram(u16 addr, u8 data) { - u32 result; + int result; u8 addr_low, addr_high, data_low, data_high; union uw_ec_write_return output; @@ -371,7 +371,7 @@ module_wmi_driver(uniwill_wmi_driver); MODULE_AUTHOR("TUXEDO Computers GmbH "); MODULE_DESCRIPTION("Driver for Uniwill WMI interface"); -MODULE_VERSION("0.0.2"); +MODULE_VERSION("0.0.3"); MODULE_LICENSE("GPL"); /* diff --git a/tuxedo_keyboard.conf b/tuxedo_keyboard.conf index 6bc2838..b006381 100644 --- a/tuxedo_keyboard.conf +++ b/tuxedo_keyboard.conf @@ -1 +1 @@ -options tuxedo-keyboard mode=0 brightness=75 color_left=0xFFFFFF color_center=0xFFFFFF color_right=0xFFFFFF +options tuxedo-keyboard kbd_backlight_mode=0