Merge remote-tracking branch 'origin/generalize_keyboard_backlight_brightness' into upcoming-release

This commit is contained in:
Christoffer Sandberg 2023-02-23 09:45:58 +01:00
commit b963225cf7
No known key found for this signature in database
GPG key ID: BF563F71B6C7A96D
12 changed files with 1286 additions and 1106 deletions

View file

@ -32,7 +32,7 @@ struct clevo_acpi_driver_data_t {
static struct clevo_acpi_driver_data_t *active_driver_data = NULL; 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; u32 status;
acpi_handle handle; acpi_handle handle;
@ -69,23 +69,17 @@ static u32 clevo_acpi_evaluate(struct acpi_device *device, u8 cmd, u32 arg, u32
if (!out_obj) { if (!out_obj) {
pr_err("failed to evaluate _DSM\n"); pr_err("failed to evaluate _DSM\n");
status = -1; status = -1;
} else { }
if (out_obj->type == ACPI_TYPE_INTEGER) { else {
if (!IS_ERR_OR_NULL(result)) if (!IS_ERR_OR_NULL(result)) {
*result = (u32) out_obj->integer.value; *result = out_obj;
// pr_debug("evaluate _DSM cmd: %0#4x arg: %0#10x\n", cmd, arg);
} else {
pr_err("unknown output from _DSM\n");
status = -ENODATA;
} }
} }
ACPI_FREE(out_obj);
return status; 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; 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) void clevo_acpi_notify(struct acpi_device *device, u32 event)
{ {
u32 event_value; u32 event_value;
union acpi_object *out_obj;
u32 status;
// struct clevo_acpi_driver_data_t *clevo_acpi_driver_data; // 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); 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); // clevo_acpi_driver_data = container_of(&device, struct clevo_acpi_driver_data_t, adev);

View file

@ -20,6 +20,7 @@
#define CLEVO_INTERFACES_H #define CLEVO_INTERFACES_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/acpi.h>
#define CLEVO_WMI_EVENT_GUID "ABBC0F6B-8EA1-11D1-00A0-C90629100000" #define CLEVO_WMI_EVENT_GUID "ABBC0F6B-8EA1-11D1-00A0-C90629100000"
#define CLEVO_WMI_EMAIL_GUID "ABBC0F6C-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_FLIGHTMODE_SW 0x07
#define CLEVO_CMD_GET_TOUCHPAD_SW 0x09 #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 // The clevo set commands expect a parameter
#define CLEVO_CMD_SET_FANSPEED_VALUE 0x68 #define CLEVO_CMD_SET_FANSPEED_VALUE 0x68
#define CLEVO_CMD_SET_FANSPEED_AUTO 0x69 #define CLEVO_CMD_SET_FANSPEED_AUTO 0x69
@ -45,18 +56,29 @@
#define CLEVO_CMD_SET_FLIGHTMODE_SW 0x20 #define CLEVO_CMD_SET_FLIGHTMODE_SW 0x20
#define CLEVO_CMD_SET_TOUCHPAD_SW 0x2a #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_CMD_OPT 0x79
#define CLEVO_OPT_SUBCMD_SET_PERF_PROF 0x19 #define CLEVO_CMD_OPT_SUB_SET_PERF_PROF 0x19
struct clevo_interface_t { struct clevo_interface_t {
char *string_id; char *string_id;
void (*event_callb)(u32); 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_add_interface(struct clevo_interface_t *new_interface);
u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface); u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface);
u32 clevo_evaluate_method(u8 cmd, u32 arg, u32 *result); 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); u32 clevo_get_active_interface_id(char **id_str);
#define MODULE_ALIAS_CLEVO_WMI() \ #define MODULE_ALIAS_CLEVO_WMI() \

File diff suppressed because it is too large Load diff

424
src/clevo_leds.h Normal file
View file

@ -0,0 +1,424 @@
/*!
* Copyright (c) 2018-2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef CLEVO_LEDS_H
#define CLEVO_LEDS_H
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
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 <linux/led-class-multicolor.h>
#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

View file

@ -23,7 +23,7 @@
#include <linux/version.h> #include <linux/version.h>
#include "clevo_interfaces.h" #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), struct acpi_buffer acpi_buffer_in = { (acpi_size)sizeof(wmi_arg),
&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) { if (!acpi_result) {
pr_err("failed to evaluate WMI method\n"); pr_err("failed to evaluate WMI method\n");
return_status = -1; return_status = -1;
} else { }
if (acpi_result->type == ACPI_TYPE_INTEGER) { else {
if (!IS_ERR_OR_NULL(result)) { if (!IS_ERR_OR_NULL(result)) {
*result = (u32)acpi_result->integer.value; *result = acpi_result;
/*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;
} }
} }
kfree(acpi_result);
return return_status; 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); 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) static int clevo_wmi_probe(struct wmi_device *wdev, const void *dummy_context)
#endif #endif
{ {
u32 status, ret; u32 status;
union acpi_object *out_obj;
pr_debug("clevo_wmi driver probe\n"); 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) // Since the WMI GUIDs aren't unique let's (at least)
// check the return of some "known existing general" method // 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) { if (status < 0) {
pr_debug("probe: Clevo GUIDs present but method call failed\n"); pr_debug("probe: Clevo GUIDs present but method call failed\n");
return -ENODEV; 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( pr_debug(
"probe: Clevo GUIDs present but method returned unexpected value\n"); "probe: Clevo GUIDs present but method returned unexpected value\n");
ACPI_FREE(out_obj);
return -ENODEV; return -ENODEV;
} }
ACPI_FREE(out_obj);
// Add this interface // Add this interface
clevo_keyboard_add_interface(&clevo_wmi_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) static void clevo_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)
{ {
u32 event_value; 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"); pr_debug("clevo_wmi notify\n");
if (!IS_ERR_OR_NULL(clevo_wmi_interface.event_callb)) { if (!IS_ERR_OR_NULL(clevo_wmi_interface.event_callb)) {
// Execute registered callback // Execute registered callback

View file

@ -254,7 +254,7 @@ static long clevo_ioctl_interface(struct file *file, unsigned int cmd, unsigned
break; break;
case W_CL_PERF_PROFILE: case W_CL_PERF_PROFILE:
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument)); 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); clevo_evaluate_method(CLEVO_CMD_OPT, clevo_arg, &result);
break; break;
} }
@ -811,6 +811,8 @@ static int __init tuxedo_io_init(void)
id_check_uniwill = uniwill_identify(); id_check_uniwill = uniwill_identify();
#ifdef DEBUG #ifdef DEBUG
pr_debug("DEBUG is defined\n");
if (id_check_clevo == 0 && id_check_uniwill == 0) { if (id_check_clevo == 0 && id_check_uniwill == 0) {
pr_debug("No matching hardware found on module load\n"); pr_debug("No matching hardware found on module load\n");
} }

View file

@ -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, &param_ops_brightness_ops, &param_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 #endif

View file

@ -35,11 +35,40 @@
#define UNIWILL_INTERFACE_WMI_STRID "uniwill_wmi" #define UNIWILL_INTERFACE_WMI_STRID "uniwill_wmi"
typedef u32 (uniwill_read_ec_ram_t)(u16, u8*); typedef int (uniwill_read_ec_ram_t)(u16, u8*);
typedef u32 (uniwill_write_ec_ram_t)(u16, u8); typedef int (uniwill_write_ec_ram_t)(u16, u8);
typedef u32 (uniwill_write_ec_ram_with_retry_t)(u16, u8, int); typedef int (uniwill_write_ec_ram_with_retry_t)(u16, u8, int);
typedef void (uniwill_event_callb_t)(u32); 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 { struct uniwill_interface_t {
char *string_id; char *string_id;
uniwill_event_callb_t *event_callb; uniwill_event_callb_t *event_callb;
@ -47,6 +76,13 @@ struct uniwill_interface_t {
uniwill_write_ec_ram_t *write_ec_ram; 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_PF5LUXG 0x09
#define UW_MODEL_PH4TUX 0x13 #define UW_MODEL_PH4TUX 0x13
#define UW_MODEL_PH4TRX 0x12 #define UW_MODEL_PH4TRX 0x12
@ -72,12 +108,6 @@ struct uniwill_device_features_t {
bool uniwill_has_charging_profile; 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); struct uniwill_device_features_t *uniwill_get_device_features(void);
union uw_ec_read_return { union uw_ec_read_return {

View file

@ -16,6 +16,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this software. If not, see <https://www.gnu.org/licenses/>. * along with this software. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef UNIWILL_KEYBOARD_H
#define UNIWILL_KEYBOARD_H
#include "tuxedo_keyboard_common.h" #include "tuxedo_keyboard_common.h"
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/wmi.h> #include <linux/wmi.h>
@ -24,17 +27,11 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/led-class-multicolor.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/version.h> #include <linux/version.h>
#include "uniwill_interfaces.h" #include "uniwill_interfaces.h"
#include "uniwill_leds.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"
#define UNIWILL_OSD_RADIOON 0x01A #define UNIWILL_OSD_RADIOON 0x01A
#define UNIWILL_OSD_RADIOOFF 0x01B #define UNIWILL_OSD_RADIOOFF 0x01B
@ -44,6 +41,7 @@
#define UNIWILL_OSD_KB_LED_LEVEL3 0x03E #define UNIWILL_OSD_KB_LED_LEVEL3 0x03E
#define UNIWILL_OSD_KB_LED_LEVEL4 0x03F #define UNIWILL_OSD_KB_LED_LEVEL4 0x03F
#define UNIWILL_OSD_DC_ADAPTER_CHANGE 0x0AB #define UNIWILL_OSD_DC_ADAPTER_CHANGE 0x0AB
#define UNIWILL_OSD_MODE_CHANGE_KEY_EVENT 0x0B0
#define UNIWILL_KEY_RFKILL 0x0A4 #define UNIWILL_KEY_RFKILL 0x0A4
#define UNIWILL_KEY_KBDILLUMDOWN 0x0B1 #define UNIWILL_KEY_KBDILLUMDOWN 0x0B1
@ -52,30 +50,16 @@
#define UNIWILL_OSD_TOUCHPADWORKAROUND 0xFFF #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_priority_write_state(void);
static void uw_charging_profile_write_state(void); static void uw_charging_profile_write_state(void);
struct tuxedo_keyboard_driver uniwill_keyboard_driver; 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; struct uniwill_device_features_t uniwill_device_features;
static bool uw_feats_loaded = false; static bool uw_feats_loaded = false;
static u8 uniwill_kbd_bl_enable_state_on_start; 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[] = { static struct key_entry uniwill_wmi_keymap[] = {
// { KE_KEY, UNIWILL_OSD_RADIOON, { KEY_RFKILL } }, // { 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_KBDILLUMDOWN, { KEY_KBDILLUMDOWN } },
{ KE_KEY, UNIWILL_KEY_KBDILLUMUP, { KEY_KBDILLUMUP } }, { KE_KEY, UNIWILL_KEY_KBDILLUMUP, { KEY_KBDILLUMUP } },
{ KE_KEY, UNIWILL_KEY_KBDILLUMTOGGLE, { KEY_KBDILLUMTOGGLE } }, { 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 // Only used to put ev bits
{ KE_KEY, 0xffff, { KEY_F6 } }, { KE_KEY, 0xffff, { KEY_F6 } },
{ KE_KEY, 0xffff, { KEY_LEFTALT } }, { KE_KEY, 0xffff, { KEY_LEFTALT } },
@ -101,9 +90,9 @@ static struct uniwill_interfaces_t {
uniwill_event_callb_t uniwill_event_callb; 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)) if (!IS_ERR_OR_NULL(uniwill_interfaces.wmi))
status = uniwill_interfaces.wmi->read_ec_ram(address, data); 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); 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)) if (!IS_ERR_OR_NULL(uniwill_interfaces.wmi))
status = uniwill_interfaces.wmi->write_ec_ram(address, data); 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); 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; u32 status;
int i; int i;
@ -159,7 +148,7 @@ EXPORT_SYMBOL(uniwill_write_ec_ram_with_retry);
static DEFINE_MUTEX(uniwill_interface_modification_lock); 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); mutex_lock(&uniwill_interface_modification_lock);
@ -181,7 +170,7 @@ u32 uniwill_add_interface(struct uniwill_interface_t *interface)
} }
EXPORT_SYMBOL(uniwill_add_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); mutex_lock(&uniwill_interface_modification_lock);
@ -201,7 +190,7 @@ u32 uniwill_remove_interface(struct uniwill_interface_t *interface)
} }
EXPORT_SYMBOL(uniwill_remove_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)) if (IS_ERR_OR_NULL(uniwill_interfaces.wmi))
return -ENODEV; return -ENODEV;
@ -258,89 +247,15 @@ static struct notifier_block keyboard_notifier_block = {
.notifier_call = keyboard_notifier_callb .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) static void uniwill_write_kbd_bl_enable(u8 enable)
{ {
u8 backlight_data; u8 backlight_data;
enable = enable & 0x01; 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 = backlight_data & ~(1 << 1);
backlight_data |= (!enable << 1); backlight_data |= (!enable << 1);
uniwill_write_ec_ram(0x078c, backlight_data); uniwill_write_ec_ram(UW_EC_REG_KBD_BL_STATUS, 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);
} }
void uniwill_event_callb(u32 code) 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 // 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_LEFTMETA, 1);
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 1); input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 1);
input_report_key(uniwill_keyboard_driver.input_device, KEY_F6, 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); input_sync(uniwill_keyboard_driver.input_device);
} }
// Keyboard backlight brightness toggle // Refresh keyboard state and charging prio on cable switch event
if (uniwill_kbd_bl_type_rgb_single_color) { if (code == UNIWILL_OSD_DC_ADAPTER_CHANGE) {
switch (code) { uniwill_leds_restore_state_extern();
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;
}
}
switch (code) {
case UNIWILL_OSD_DC_ADAPTER_CHANGE:
msleep(50); msleep(50);
uw_charging_priority_write_state(); uw_charging_priority_write_state();
break;
} }
} }
static ssize_t uw_brightness_show(struct device *child, static void uw_kbd_bl_init_set(struct platform_device *dev)
struct device_attribute *attr, char *buffer)
{ {
return sprintf(buffer, "%d\n", kbd_led_state_uw.brightness); uniwill_leds_init_late(dev);
}
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_write_kbd_bl_enable(1); 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 volatile int uw_kbd_bl_check_count = 40;
static int uw_kbd_bl_init_check_interval_ms = 500; 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) static void uw_kbd_bl_init_ready_check_work_func(struct work_struct *work)
{ {
u8 uw_cur_red, uw_cur_green, uw_cur_blue; 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) { 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); del_timer(&uw_kbd_bl_init_timer);
} else { } else {
if (uw_kbd_bl_check_count != 0) { if (uw_kbd_bl_check_count != 0) {
@ -540,51 +364,15 @@ static int uw_kbd_bl_init(struct platform_device *dev)
{ {
int status = 0; 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) #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
TUXEDO_ERROR( TUXEDO_ERROR("Warning: Kernel version less that 4.18, keyboard backlight might not be properly recognized.");
"Warning: Kernel version less that 4.18, keyboard backlight might not be properly recognized.");
#endif #endif
// Save previous enable state uniwill_leds_init_early(dev);
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");
if (uniwill_leds_get_backlight_type() == UNIWILL_KB_BACKLIGHT_TYPE_1_ZONE_RGB) {
// Start periodic checking of animation, set and enable bl when done // 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); 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)); mod_timer(&uw_kbd_bl_init_timer, jiffies + msecs_to_jiffies(uw_kbd_bl_init_check_interval_ms));
} else { } else {
@ -1291,9 +1079,7 @@ static int uniwill_keyboard_remove(struct platform_device *dev)
if (uw_charging_profile_loaded) if (uw_charging_profile_loaded)
sysfs_remove_group(&dev->dev.kobj, &uw_charging_profile_attr_group); sysfs_remove_group(&dev->dev.kobj, &uw_charging_profile_attr_group);
if (uniwill_kbd_bl_type_rgb_single_color) { uniwill_leds_remove(dev);
sysfs_remove_group(&dev->dev.kobj, &uw_kbd_bl_color_attr_group);
}
// Restore previous backlight enable state // Restore previous backlight enable state
if (uniwill_kbd_bl_enable_state_on_start != 0xff) { 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) static int uniwill_keyboard_resume(struct platform_device *dev)
{ {
if (uniwill_kbd_bl_type_rgb_single_color) { uniwill_leds_restore_state_extern();
uniwill_write_kbd_bl_reset();
msleep(100);
uniwill_write_kbd_bl_state();
}
uniwill_write_kbd_bl_enable(1); uniwill_write_kbd_bl_enable(1);
return 0; return 0;
} }
@ -1346,3 +1128,5 @@ struct tuxedo_keyboard_driver uniwill_keyboard_driver = {
.probe = uniwill_keyboard_probe, .probe = uniwill_keyboard_probe,
.key_map = uniwill_wmi_keymap, .key_map = uniwill_wmi_keymap,
}; };
#endif // UNIWILL_KEYBOARD_H

329
src/uniwill_leds.h Normal file
View file

@ -0,0 +1,329 @@
/*!
* Copyright (c) 2018-2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef UNIWILL_LEDS_H
#define UNIWILL_LEDS_H
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/completion.h>
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 <linux/led-class-multicolor.h>
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

View file

@ -43,11 +43,11 @@ static bool uniwill_ec_direct = true;
DEFINE_MUTEX(uniwill_ec_lock); 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; acpi_status status;
union acpi_object *out_acpi; union acpi_object *out_acpi;
u32 e_result = 0; int e_result = 0;
// Kernel buffer for input argument // Kernel buffer for input argument
u32 *wmi_arg = (u32 *) kmalloc(sizeof(u32)*10, GFP_KERNEL); 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 * 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 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]; 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); // 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; 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 * 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 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]; output->dword = uw_data[0];
return ret; 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 * 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; u8 tmp, count, flags;
bool ready; bool ready;
bool bflag = false; 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; 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; u8 tmp, count, flags;
bool ready; bool ready;
bool bflag = false; 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; 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; u8 addr_low, addr_high;
union uw_ec_read_return output; union uw_ec_read_return output;
@ -261,9 +261,9 @@ u32 uw_wmi_read_ec_ram(u16 addr, u8 *data)
return result; 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; u8 addr_low, addr_high, data_low, data_high;
union uw_ec_write_return output; union uw_ec_write_return output;

View file

@ -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