diff --git a/src/uniwill_keyboard.h b/src/uniwill_keyboard.h index 6b5dcdc..5d1a581 100644 --- a/src/uniwill_keyboard.h +++ b/src/uniwill_keyboard.h @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include "uw_io.h" #define UNIWILL_WMI_MGMT_GUID_BA "ABBC0F6D-8EA1-11D1-00A0-C90629100000" #define UNIWILL_WMI_MGMT_GUID_BB "ABBC0F6E-8EA1-11D1-00A0-C90629100000" @@ -43,6 +46,8 @@ #define UNIWILL_OSD_DC_ADAPTER_CHANGE 0x0AB #define UNIWILL_KEY_RFKILL 0x0A4 +#define UNIWILL_KEY_KBDILLUMDOWN 0x0B1 +#define UNIWILL_KEY_KBDILLUMUP 0x0B2 #define UNIWILL_OSD_TOUCHPADWORKAROUND 0xFFF @@ -51,30 +56,6 @@ #define UNIWILL_BRIGHTNESS_DEFAULT UNIWILL_BRIGHTNESS_MAX * 0.30 #define UNIWILL_COLOR_DEFAULT 0xffffff -union uw_ec_read_return { - u32 dword; - struct { - u8 data_low; - u8 data_high; - } bytes; -}; - -union uw_ec_write_return { - u32 dword; - struct { - u8 addr_low; - u8 addr_high; - u8 data_low; - u8 data_high; - } bytes; -}; - -typedef u32 (uw_ec_read_func)(u8, u8, union uw_ec_read_return *); -typedef u32 (uw_ec_write_func)(u8, u8, u8, u8, union uw_ec_write_return *); - -extern uw_ec_read_func uw_ec_read_addr; -extern uw_ec_write_func uw_ec_write_addr; - struct tuxedo_keyboard_driver uniwill_keyboard_driver; struct kbd_led_state_uw_t { @@ -95,6 +76,9 @@ static struct key_entry uniwill_wmi_keymap[] = { // Manual mode rfkill { KE_KEY, UNIWILL_KEY_RFKILL, { KEY_RFKILL }}, { KE_KEY, UNIWILL_OSD_TOUCHPADWORKAROUND, { KEY_F21 } }, + // Keyboard brightness + { KE_KEY, UNIWILL_KEY_KBDILLUMDOWN, { KEY_KBDILLUMDOWN } }, + { KE_KEY, UNIWILL_KEY_KBDILLUMUP, { KEY_KBDILLUMUP } }, // Only used to put ev bits { KE_KEY, 0xffff, { KEY_F6 } }, { KE_KEY, 0xffff, { KEY_LEFTALT } }, @@ -131,7 +115,7 @@ static int keyboard_notifier_callb(struct notifier_block *nb, unsigned long code if (prevprev_key == 85 && prev_key == 29) { TUXEDO_DEBUG("Touchpad Toggle\n"); schedule_work(&uniwill_key_event_work); - ret = NOTIFY_STOP; + ret = NOTIFY_OK; } break; } @@ -150,22 +134,11 @@ static struct notifier_block keyboard_notifier_block = { static u8 uniwill_read_kbd_bl_enabled(void) { union uw_ec_read_return reg_read_return; - - uw_ec_read_func *__uw_ec_read_addr; - u8 enabled = 0xff; - __uw_ec_read_addr = symbol_get(uw_ec_read_addr); - - if (__uw_ec_read_addr) { - __uw_ec_read_addr(0x8c, 0x07, ®_read_return); - enabled = (reg_read_return.bytes.data_low >> 1) & 0x01; - enabled = !enabled; - } else { - TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); - } - - if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr); + __uw_ec_read_addr(0x8c, 0x07, ®_read_return); + enabled = (reg_read_return.bytes.data_low >> 1) & 0x01; + enabled = !enabled; return enabled; } @@ -174,27 +147,13 @@ static void uniwill_write_kbd_bl_enable(u8 enable) { union uw_ec_read_return reg_read_return; union uw_ec_write_return reg_write_return; - - uw_ec_read_func *__uw_ec_read_addr; - uw_ec_write_func *__uw_ec_write_addr; - u8 write_value = 0; enable = enable & 0x01; - __uw_ec_read_addr = symbol_get(uw_ec_read_addr); - __uw_ec_write_addr = symbol_get(uw_ec_write_addr); - - if (__uw_ec_read_addr && __uw_ec_write_addr) { - __uw_ec_read_addr(0x8c, 0x07, ®_read_return); - write_value = reg_read_return.bytes.data_low & ~(1 << 1); - write_value |= (!enable << 1); - __uw_ec_write_addr(0x8c, 0x07, write_value, 0x00, ®_write_return); - } else { - TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); - } - - if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr); - if (__uw_ec_write_addr) symbol_put(uw_ec_write_addr); + __uw_ec_read_addr(0x8c, 0x07, ®_read_return); + write_value = reg_read_return.bytes.data_low & ~(1 << 1); + write_value |= (!enable << 1); + __uw_ec_write_addr(0x8c, 0x07, write_value, 0x00, ®_write_return); } /*static u32 uniwill_read_kbd_bl_br_state(u8 *brightness_state) @@ -202,18 +161,9 @@ static void uniwill_write_kbd_bl_enable(u8 enable) union uw_ec_read_return reg_read_return; u32 result; - uw_ec_read_func *__uw_ec_read_addr; - - __uw_ec_read_addr = symbol_get(uw_ec_read_addr); - - if (__uw_ec_read_addr) { - __uw_ec_read_addr(0x8c, 0x07, ®_read_return); - *brightness_state = (reg_read_return.bytes.data_low & 0xf0) >> 4; - result = 0; - } else { - TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); - result = -EIO; - } + __uw_ec_read_addr(0x8c, 0x07, ®_read_return); + *brightness_state = (reg_read_return.bytes.data_low & 0xf0) >> 4; + result = 0; return result; }*/ @@ -223,24 +173,13 @@ static u32 uniwill_read_kbd_bl_rgb(u8 *red, u8 *green, u8 *blue) union uw_ec_read_return reg_read_return; u32 result; - uw_ec_read_func *__uw_ec_read_addr; - - __uw_ec_read_addr = symbol_get(uw_ec_read_addr); - - if (__uw_ec_read_addr) { - __uw_ec_read_addr(0x03, 0x18, ®_read_return); - *red = reg_read_return.bytes.data_low; - __uw_ec_read_addr(0x05, 0x18, ®_read_return); - *green = reg_read_return.bytes.data_low; - __uw_ec_read_addr(0x08, 0x18, ®_read_return); - *blue = reg_read_return.bytes.data_low; - result = 0; - } else { - TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); - result = -EIO; - } - - if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr); + __uw_ec_read_addr(0x03, 0x18, ®_read_return); + *red = reg_read_return.bytes.data_low; + __uw_ec_read_addr(0x05, 0x18, ®_read_return); + *green = reg_read_return.bytes.data_low; + __uw_ec_read_addr(0x08, 0x18, ®_read_return); + *blue = reg_read_return.bytes.data_low; + result = 0; return result; } @@ -249,27 +188,14 @@ static void uniwill_write_kbd_bl_rgb(u8 red, u8 green, u8 blue) { union uw_ec_write_return reg_write_return; - uw_ec_read_func *__uw_ec_read_addr; - uw_ec_write_func *__uw_ec_write_addr; - - __uw_ec_read_addr = symbol_get(uw_ec_read_addr); - __uw_ec_write_addr = symbol_get(uw_ec_write_addr); - - if (__uw_ec_read_addr && __uw_ec_write_addr) { - // Write the colors - if (red > 0xc8) red = 0xc8; - if (green > 0xc8) green = 0xc8; - if (blue > 0xc8) blue = 0xc8; - __uw_ec_write_addr(0x03, 0x18, red, 0x00, ®_write_return); - __uw_ec_write_addr(0x05, 0x18, green, 0x00, ®_write_return); - __uw_ec_write_addr(0x08, 0x18, blue, 0x00, ®_write_return); - TUXEDO_DEBUG("Wrote color [%0#4x, %0#4x, %0#4x]\n", red, green, blue); - } else { - TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); - } - - if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr); - if (__uw_ec_write_addr) symbol_put(uw_ec_write_addr); + // Write the colors + if (red > 0xc8) red = 0xc8; + if (green > 0xc8) green = 0xc8; + if (blue > 0xc8) blue = 0xc8; + __uw_ec_write_addr(0x03, 0x18, red, 0x00, ®_write_return); + __uw_ec_write_addr(0x05, 0x18, green, 0x00, ®_write_return); + __uw_ec_write_addr(0x08, 0x18, blue, 0x00, ®_write_return); + TUXEDO_DEBUG("Wrote color [%0#4x, %0#4x, %0#4x]\n", red, green, blue); } static void uniwill_write_kbd_bl_state(void) { @@ -296,22 +222,8 @@ static void uniwill_write_kbd_bl_state(void) { static void uniwill_write_kbd_bl_reset(void) { union uw_ec_write_return reg_write_return; - union uw_ec_read_return reg_read_return; - uw_ec_read_func *__uw_ec_read_addr; - uw_ec_write_func *__uw_ec_write_addr; - - __uw_ec_read_addr = symbol_get(uw_ec_read_addr); - __uw_ec_write_addr = symbol_get(uw_ec_write_addr); - - if (__uw_ec_read_addr && __uw_ec_write_addr) { - __uw_ec_write_addr(0x8c, 0x07, 0x10, 0x00, ®_write_return); - } else { - TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); - } - - if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr); - if (__uw_ec_write_addr) symbol_put(uw_ec_write_addr); + __uw_ec_write_addr(0x8c, 0x07, 0x10, 0x00, ®_write_return); } static void uniwill_wmi_handle_event(u32 value, void *context, u32 guid_nr) @@ -585,6 +497,232 @@ static int uw_kbd_bl_init(struct platform_device *dev) return status; } +#define UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS 0x24 +#define UNIWILL_LIGHTBAR_LED_NAME_RGB_RED "lightbar_rgb:1:status" +#define UNIWILL_LIGHTBAR_LED_NAME_RGB_GREEN "lightbar_rgb:2:status" +#define UNIWILL_LIGHTBAR_LED_NAME_RGB_BLUE "lightbar_rgb:3:status" +#define UNIWILL_LIGHTBAR_LED_NAME_ANIMATION "lightbar_animation::status" + +static void uniwill_write_lightbar_rgb(u8 red, u8 green, u8 blue) +{ + union uw_ec_write_return reg_write_return; + + uw_ec_write_func *__uw_ec_write_addr; + + __uw_ec_write_addr = symbol_get(uw_ec_write_addr); + + if (__uw_ec_write_addr) { + if (red <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) { + __uw_ec_write_addr(0x49, 0x07, red, 0x00, ®_write_return); + } + if (green <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) { + __uw_ec_write_addr(0x4a, 0x07, green, 0x00, ®_write_return); + } + if (blue <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) { + __uw_ec_write_addr(0x4b, 0x07, blue, 0x00, ®_write_return); + } + } else { + TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); + } + + if (__uw_ec_write_addr) symbol_put(uw_ec_write_addr); +} + +static int uniwill_read_lightbar_rgb(u8 *red, u8 *green, u8 *blue) +{ + int status; + union uw_ec_read_return reg_read_return; + + uw_ec_read_func *__uw_ec_read_addr; + + __uw_ec_read_addr = symbol_get(uw_ec_read_addr); + + if (__uw_ec_read_addr) { + __uw_ec_read_addr(0x49, 0x07, ®_read_return); + *red = reg_read_return.bytes.data_low; + __uw_ec_read_addr(0x4a, 0x07, ®_read_return); + *green = reg_read_return.bytes.data_low; + __uw_ec_read_addr(0x4b, 0x07, ®_read_return); + *blue = reg_read_return.bytes.data_low; + status = 0; + } else { + status = -EIO; + TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); + } + + if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr); + + return status; +} + +static void uniwill_write_lightbar_animation(bool animation_status) +{ + union uw_ec_write_return reg_write_return; + union uw_ec_read_return reg_read_return; + + u8 value; + + uw_ec_write_func *__uw_ec_write_addr; + uw_ec_read_func *__uw_ec_read_addr; + + __uw_ec_write_addr = symbol_get(uw_ec_write_addr); + __uw_ec_read_addr = symbol_get(uw_ec_read_addr); + + if (__uw_ec_write_addr && __uw_ec_read_addr) { + __uw_ec_read_addr(0x48, 0x07, ®_read_return); + value = reg_read_return.bytes.data_low; + if (animation_status) { + value |= 0x80; + } else { + value &= ~0x80; + } + __uw_ec_write_addr(0x48, 0x07, value, 0x00, ®_write_return); + } else { + TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); + } + + if (__uw_ec_write_addr) symbol_put(uw_ec_write_addr); + if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr); +} + +static int uniwill_read_lightbar_animation(bool *animation_status) +{ + int status; + union uw_ec_read_return reg_read_return; + + uw_ec_read_func *__uw_ec_read_addr; + + __uw_ec_read_addr = symbol_get(uw_ec_read_addr); + + if (__uw_ec_read_addr) { + __uw_ec_read_addr(0x48, 0x07, ®_read_return); + *animation_status = (reg_read_return.bytes.data_low & 0x80) > 0; + status = 0; + } else { + status = -EIO; + TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n"); + } + + if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr); + + return status; +} + +static int lightbar_set_blocking(struct led_classdev *led_cdev, enum led_brightness brightness) +{ + u8 red = 0xff, green = 0xff, blue = 0xff; + bool led_red = strstr(led_cdev->name, UNIWILL_LIGHTBAR_LED_NAME_RGB_RED) != NULL; + bool led_green = strstr(led_cdev->name, UNIWILL_LIGHTBAR_LED_NAME_RGB_GREEN) != NULL; + bool led_blue = strstr(led_cdev->name, UNIWILL_LIGHTBAR_LED_NAME_RGB_BLUE) != NULL; + bool led_animation = strstr(led_cdev->name, UNIWILL_LIGHTBAR_LED_NAME_ANIMATION) != NULL; + + if (led_red || led_green || led_blue) { + if (led_red) { + red = brightness; + } else if (led_green) { + green = brightness; + } else if (led_blue) { + blue = brightness; + } + uniwill_write_lightbar_rgb(red, green, blue); + // Also make sure the animation is off + uniwill_write_lightbar_animation(false); + } else if (led_animation) { + if (brightness == 1) { + uniwill_write_lightbar_animation(true); + } else { + uniwill_write_lightbar_animation(false); + } + } + return 0; +} + +static enum led_brightness lightbar_get(struct led_classdev *led_cdev) +{ + u8 red, green, blue; + bool animation_status; + bool led_red = strstr(led_cdev->name, UNIWILL_LIGHTBAR_LED_NAME_RGB_RED) != NULL; + bool led_green = strstr(led_cdev->name, UNIWILL_LIGHTBAR_LED_NAME_RGB_GREEN) != NULL; + bool led_blue = strstr(led_cdev->name, UNIWILL_LIGHTBAR_LED_NAME_RGB_BLUE) != NULL; + bool led_animation = strstr(led_cdev->name, UNIWILL_LIGHTBAR_LED_NAME_ANIMATION) != NULL; + + if (led_red || led_green || led_blue) { + uniwill_read_lightbar_rgb(&red, &green, &blue); + if (led_red) { + return red; + } else if (led_green) { + return green; + } else if (led_blue) { + return blue; + } + } else if (led_animation) { + uniwill_read_lightbar_animation(&animation_status); + return animation_status ? 1 : 0; + } + + return 0; +} + +static bool uw_lightbar_loaded; +static struct led_classdev lightbar_led_classdevs[] = { + { + .name = UNIWILL_LIGHTBAR_LED_NAME_RGB_RED, + .max_brightness = UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS, + .brightness_set_blocking = &lightbar_set_blocking, + .brightness_get = &lightbar_get + }, + { + .name = UNIWILL_LIGHTBAR_LED_NAME_RGB_GREEN, + .max_brightness = UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS, + .brightness_set_blocking = &lightbar_set_blocking, + .brightness_get = &lightbar_get + }, + { + .name = UNIWILL_LIGHTBAR_LED_NAME_RGB_BLUE, + .max_brightness = UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS, + .brightness_set_blocking = &lightbar_set_blocking, + .brightness_get = &lightbar_get + }, + { + .name = UNIWILL_LIGHTBAR_LED_NAME_ANIMATION, + .max_brightness = 1, + .brightness_set_blocking = &lightbar_set_blocking, + .brightness_get = &lightbar_get + } +}; + +static int uw_lightbar_init(struct platform_device *dev) +{ + int i, j, status; + + bool lightbar_supported = false + || dmi_match(DMI_BOARD_NAME, "LAPQC71A") + || dmi_match(DMI_BOARD_NAME, "LAPQC71B") + ; + if (!lightbar_supported) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(lightbar_led_classdevs); ++i) { + status = led_classdev_register(&dev->dev, &lightbar_led_classdevs[i]); + if (status < 0) { + for (j = 0; j < i; j++) + led_classdev_unregister(&lightbar_led_classdevs[j]); + return status; + } + } + + return 0; +} + +static int uw_lightbar_remove(struct platform_device *dev) +{ + int i; + for (i = 0; i < ARRAY_SIZE(lightbar_led_classdevs); ++i) { + led_classdev_unregister(&lightbar_led_classdevs[i]); + } + return 0; +} + static int uniwill_keyboard_probe(struct platform_device *dev) { int status; @@ -626,6 +764,9 @@ static int uniwill_keyboard_probe(struct platform_device *dev) uw_kbd_bl_init(dev); + status = uw_lightbar_init(dev); + uw_lightbar_loaded = (status >= 0); + return 0; err_remove_notifiers: @@ -638,10 +779,13 @@ err_remove_notifiers: static int uniwill_keyboard_remove(struct platform_device *dev) { + + if (uniwill_kbd_bl_type_rgb_single_color) { + sysfs_remove_group(&dev->dev.kobj, &uw_kbd_bl_color_attr_group); + } + // Restore previous backlight enable state if (uniwill_kbd_bl_enable_state_on_start != 0xff) { - if (uniwill_kbd_bl_type_rgb_single_color) - sysfs_remove_group(&dev->dev.kobj, &uw_kbd_bl_color_attr_group); uniwill_write_kbd_bl_enable(uniwill_kbd_bl_enable_state_on_start); } @@ -652,6 +796,9 @@ static int uniwill_keyboard_remove(struct platform_device *dev) del_timer(&uw_kbd_bl_init_timer); + if (uw_lightbar_loaded) + uw_lightbar_remove(dev); + return 0; } diff --git a/src/uw_io.h b/src/uw_io.h new file mode 100644 index 0000000..ea4b639 --- /dev/null +++ b/src/uw_io.h @@ -0,0 +1,102 @@ +/* +* uw_io.h +* +* Copyright (C) 2018-2020 TUXEDO Computers GmbH +* +* This program 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 2 of the License, or +* (at your option) any later version. +* +* This program 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 program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef UW_IO_H +#define UW_IO_H + +#include +#include + +union uw_ec_read_return { + u32 dword; + struct { + u8 data_low; + u8 data_high; + } bytes; +}; + +union uw_ec_write_return { + u32 dword; + struct { + u8 addr_low; + u8 addr_high; + u8 data_low; + u8 data_high; + } bytes; +}; + +typedef u32 (uw_ec_read_func)(u8, u8, union uw_ec_read_return *); +typedef u32 (uw_ec_write_func)(u8, u8, u8, u8, union uw_ec_write_return *); + +extern uw_ec_read_func uw_ec_read_addr; +extern uw_ec_write_func uw_ec_write_addr; + +/** + * uw ec read from tccwmi for now stuffed in this wrapper for simplification + * + * @returns The result of the tccwmi function, or -ENOENT + * if tccwmi symbol is not accessible + */ +static u32 __uw_ec_read_addr(u8 addr_low, u8 addr_high, + union uw_ec_read_return *output) +{ + uw_ec_read_func *symbol_uw_ec_read_addr; + u32 result = 0; + symbol_uw_ec_read_addr = symbol_get(uw_ec_read_addr); + if (symbol_uw_ec_read_addr) { + result = symbol_uw_ec_read_addr(addr_low, addr_high, output); + } else { + pr_debug("tuxedo-cc-wmi symbols not found\n"); + result = -ENOENT; + } + + if (symbol_uw_ec_read_addr) + symbol_put(uw_ec_read_addr); + + return result; +} + +/** + * uw ec write from tccwmi for now stuffed in this wrapper for simplification + * + * @returns The result of the tccwmi function, or -ENOENT + * if tccwmi symbol is not accessible + */ +static u32 __uw_ec_write_addr(u8 addr_low, u8 addr_high, u8 data_low, + u8 data_high, union uw_ec_write_return *output) +{ + uw_ec_write_func *symbol_uw_ec_write_addr; + u32 result = 0; + symbol_uw_ec_write_addr = symbol_get(uw_ec_write_addr); + + if (symbol_uw_ec_write_addr) { + result = symbol_uw_ec_write_addr(addr_low, addr_high, data_low, + data_high, output); + } else { + pr_debug("tuxedo-cc-wmi symbols not found\n"); + result = -ENOENT; + } + + if (symbol_uw_ec_write_addr) + symbol_put(uw_ec_write_addr); + + return result; +} + +#endif