All of lore.kernel.org
 help / color / mirror / Atom feed
From: Armin Wolf <W_Armin@gmx.de>
To: Werner Sembach <wse@tuxedocomputers.com>,
	ilpo.jarvinen@linux.intel.com, hansg@kernel.org
Cc: platform-driver-x86@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/7] platform/x86: uniwill-laptop: Add keyboard backlight support
Date: Thu, 9 Jul 2026 07:49:45 +0200	[thread overview]
Message-ID: <a681b59d-1719-4a8e-9b75-e8317c899b27@gmx.de> (raw)
In-Reply-To: <1a91a9ff-8c81-4176-a6f2-fd2a6bc49820@tuxedocomputers.com>

Am 08.07.26 um 22:15 schrieb Werner Sembach:

>
> Am 08.07.26 um 22:08 schrieb Armin Wolf:
>> Am 08.07.26 um 21:11 schrieb Werner Sembach:
>>
>>> Hi,
>>>
>>> Am 30.05.26 um 19:08 schrieb Armin Wolf:
>>>> Many Uniwill-based devices support either a white-only or fully
>>>> features RGB keyboard backlight. Add support for this feature
>>>> and handle the associated WMI events.
>>>>
>>>> Signed-off-by: Armin Wolf <W_Armin@gmx.de>
>>>> ---
>>>>   .../admin-guide/laptops/uniwill-laptop.rst    |  13 +
>>>>   drivers/platform/x86/uniwill/uniwill-acpi.c   | 392 
>>>> +++++++++++++++++-
>>>>   2 files changed, 397 insertions(+), 8 deletions(-)
>>>>
>>>> diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst 
>>>> b/Documentation/admin-guide/laptops/uniwill-laptop.rst
>>>> index 24b41dbab886..2b1e5da703a5 100644
>>>> --- a/Documentation/admin-guide/laptops/uniwill-laptop.rst
>>>> +++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst
>>>> @@ -77,6 +77,19 @@ LED class device. The default name of this LED 
>>>> class device is ``uniwill:multico
>>>>   See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for 
>>>> details on how to control the various
>>>>   animation modes of the lightbar.
>>>>   +Keyboard Backlight
>>>> +------------------
>>>> +
>>>> +The ``uniwill-laptop`` driver supports controlling the keyboard 
>>>> backlight using the standard
>>>> +LED class interface. The default name of this LED class device is 
>>>> ``uniwill:white:kbd_backlight``
>>>> +when the keyboard backlight supports only a single color, or 
>>>> ``uniwill:multicolor:kbd_backlight``
>>>> +when the keyboard backlight supports RGB colors. The maximum 
>>>> intensity for each color channel
>>>> +in RGB mode is 50.
>>>> +
>>>> +Keep in mind that due to hardware design choices, the driver does 
>>>> not support the RGB value
>>>> +``0x000000`` (black), instead it will fall back to ``0x010101`` 
>>>> (faint white). In order to
>>>> +disable the keyboard backlight, the standard LED brightness 
>>>> setting has to be used instead.
>>>> +
>>>>   Configurable TGP
>>>>   ----------------
>>>>   diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c 
>>>> b/drivers/platform/x86/uniwill/uniwill-acpi.c
>>>> index ab063ead45b9..fd040197b189 100644
>>>> --- a/drivers/platform/x86/uniwill/uniwill-acpi.c
>>>> +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
>>>> @@ -110,8 +110,31 @@
>>>>   #define EC_ADDR_BAT_CYCLE_COUNT_2    0x04A7
>>>>     #define EC_ADDR_PROJECT_ID        0x0740
>>>> +#define PROJECT_ID_NONE            0x00
>>>> +#define PROJECT_ID_GI            0x01
>>>> +#define PROJECT_ID_GJ            0x02
>>>> +#define PROJECT_ID_GK            0x03
>>>> +#define PROJECT_ID_GICN            0x04
>>>> +#define PROJECT_ID_GJCN            0x05
>>>> +#define PROJECT_ID_GK5CN_X        0x06
>>>> +#define PROJECT_ID_GK7CN_S        0x07
>>>> +#define PROJECT_ID_GK7CPCS_GK5CQ7Z    0x08
>>>> +#define PROJECT_ID_PF            0x09
>>>> +#define PROJECT_ID_GK5CP_4X_5X_6X    0x0A
>>>> +#define PROJECT_ID_IDP            0x0B
>>>> +#define PROJECT_ID_IDY_6Y        0x0C
>>>> +#define PROJECT_ID_IDY_7Y        0x0D
>>>> +#define PROJECT_ID_PF4MU_PF4MN_PF5MU    0x0E
>>>> +#define PROJECT_ID_CML_GAMING        0x0F
>>>> +#define PROJECT_ID_GK7NXXR        0x10
>>>> +#define PROJECT_ID_GM5MU1Y        0x11
>>>>   #define PROJECT_ID_PH4TRX1        0x12
>>>> +#define PROJECT_ID_PH4TUX1        0x13
>>>> +#define PROJECT_ID_PH4TQX1        0x14
>>>>   #define PROJECT_ID_PH6TRX1        0x15
>>>> +#define PROJECT_ID_PH6TQXX        0x16
>>>> +#define PROJECT_ID_PHXAXXX        0x17
>>>> +#define PROJECT_ID_PHXPXXX        0x18
>>>>     #define EC_ADDR_AP_OEM            0x0741
>>>>   #define    ENABLE_MANUAL_CTRL        BIT(0)
>>>> @@ -214,6 +237,7 @@
>>>>   #define FAN_TABLE_OFFICE_MODE        BIT(2)
>>>>   #define FAN_V3                BIT(3)
>>>>   #define DEFAULT_MODE            BIT(4)
>>>> +#define ENABLE_CHINA_MODE        BIT(6)
>>>>     #define EC_ADDR_PL1_SETTING        0x0783
>>>>   @@ -225,11 +249,11 @@
>>>>   #define FAN_CURVE_LENGTH        5
>>>>     #define EC_ADDR_KBD_STATUS        0x078C
>>>> -#define KBD_WHITE_ONLY            BIT(0)    // ~single color
>>>> -#define KBD_SINGLE_COLOR_OFF        BIT(1)
>>>> +#define KBD_WHITE_ONLY            BIT(0)
>>>> +#define KBD_POWER_OFF            BIT(1)
>>>>   #define KBD_TURBO_LEVEL_MASK        GENMASK(3, 2)
>>>>   #define KBD_APPLY            BIT(4)
>>>> -#define KBD_BRIGHTNESS            GENMASK(7, 5)
>>>> +#define KBD_BRIGHTNESS_MASK        GENMASK(7, 5)
>>>>     #define EC_ADDR_FAN_CTRL        0x078E
>>>>   #define FAN3P5                BIT(1)
>>>> @@ -320,6 +344,9 @@
>>>>   #define LED_CHANNELS        3
>>>>   #define LED_MAX_BRIGHTNESS    200
>>>>   +#define KBD_LED_CHANNELS    3
>>>> +#define KBD_LED_MAX_INTENSITY    50
>>>> +
>>>>   #define UNIWILL_FEATURE_FN_LOCK            BIT(0)
>>>>   #define UNIWILL_FEATURE_SUPER_KEY        BIT(1)
>>>>   #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE        BIT(2)
>>>> @@ -333,6 +360,7 @@
>>>>   #define UNIWILL_FEATURE_SECONDARY_FAN        BIT(9)
>>>>   #define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL    BIT(10)
>>>>   #define UNIWILL_FEATURE_USB_C_POWER_PRIORITY    BIT(11)
>>>> +#define UNIWILL_FEATURE_KEYBOARD_BACKLIGHT    BIT(12)
>>>>     enum usb_c_power_priority_options {
>>>>       USB_C_POWER_PRIORITY_CHARGING = 0,
>>>> @@ -344,6 +372,7 @@ struct uniwill_data {
>>>>       acpi_handle handle;
>>>>       struct regmap *regmap;
>>>>       unsigned int features;
>>>> +    u8 project_id;
>>>>       struct acpi_battery_hook hook;
>>>>       struct mutex battery_lock;    /* Protects the list of 
>>>> currently registered batteries */
>>>>       union {
>>>> @@ -362,6 +391,18 @@ struct uniwill_data {
>>>>       struct mutex led_lock;        /* Protects writes to the 
>>>> lightbar registers */
>>>>       struct led_classdev_mc led_mc_cdev;
>>>>       struct mc_subled led_mc_subled_info[LED_CHANNELS];
>>>> +    bool single_color_kbd;
>>>> +    u8 kbd_led_max_brightness;
>>>> +    unsigned int last_kbd_status;
>>>> +    union {
>>>> +        struct {
>>>> +            /* Protects writes to the RGB keyboard backlight 
>>>> registers */
>>>> +            struct mutex kbd_rgb_led_lock;
>>>> +            struct led_classdev_mc kbd_led_mc_cdev;
>>>> +            struct mc_subled 
>>>> kbd_led_mc_subled_info[KBD_LED_CHANNELS];
>>>> +        };
>>>> +        struct led_classdev kbd_led_cdev;
>>>> +    };
>>>>       struct mutex input_lock;    /* Protects input sequence during 
>>>> notify */
>>>>       struct input_dev *input_device;
>>>>       struct notifier_block nb;
>>>> @@ -376,6 +417,7 @@ struct uniwill_battery_entry {
>>>>     struct uniwill_device_descriptor {
>>>>       unsigned int features;
>>>> +    u8 kbd_led_max_brightness;
>>>>       /* Executed during driver probing */
>>>>       int (*probe)(struct uniwill_data *data);
>>>>   };
>>>> @@ -427,6 +469,9 @@ static const struct key_entry uniwill_keymap[] = {
>>>>       { KE_KEY,       UNIWILL_OSD_KBDILLUMDOWN, { KEY_KBDILLUMDOWN }},
>>>>       { KE_KEY,       UNIWILL_OSD_KBDILLUMUP, { KEY_KBDILLUMUP }},
>>>>   +    /* Reported when the EC changed the keyboard backlight 
>>>> brightness */
>>>> +    { KE_IGNORE,    UNIWILL_OSD_BACKLIGHT_LEVEL_CHANGE, { 
>>>> KEY_UNKNOWN }},
>>>> +
>>>>       /* Reported when the user wants to toggle the microphone mute 
>>>> status */
>>>>       { KE_KEY,       UNIWILL_OSD_MIC_MUTE, { KEY_MICMUTE }},
>>>>   @@ -435,11 +480,6 @@ static const struct key_entry 
>>>> uniwill_keymap[] = {
>>>>         /* Reported when the user wants to toggle the brightness of 
>>>> the keyboard */
>>>>       { KE_KEY,       UNIWILL_OSD_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 }},
>>>>         /* FIXME: find out the exact meaning of those events */
>>>>       { KE_IGNORE,    UNIWILL_OSD_BAT_CHARGE_FULL_24_H, { 
>>>> KEY_UNKNOWN }},
>>>> @@ -547,6 +587,11 @@ static bool uniwill_writeable_reg(struct 
>>>> device *dev, unsigned int reg)
>>>>       case EC_ADDR_LIGHTBAR_AC_BLUE:
>>>>       case EC_ADDR_BIOS_OEM:
>>>>       case EC_ADDR_TRIGGER:
>>>> +    case EC_ADDR_RGB_RED:
>>>> +    case EC_ADDR_RGB_GREEN:
>>>> +    case EC_ADDR_RGB_BLUE:
>>>> +    case EC_ADDR_BIOS_OEM_2:
>>>> +    case EC_ADDR_KBD_STATUS:
>>>>       case EC_ADDR_OEM_4:
>>>>       case EC_ADDR_CHARGE_CTRL:
>>>>       case EC_ADDR_LIGHTBAR_BAT_CTRL:
>>>> @@ -583,8 +628,14 @@ static bool uniwill_readable_reg(struct device 
>>>> *dev, unsigned int reg)
>>>>       case EC_ADDR_BIOS_OEM:
>>>>       case EC_ADDR_PWM_1:
>>>>       case EC_ADDR_PWM_2:
>>>> +    case EC_ADDR_SUPPORT_2:
>>>>       case EC_ADDR_TRIGGER:
>>>>       case EC_ADDR_SWITCH_STATUS:
>>>> +    case EC_ADDR_RGB_RED:
>>>> +    case EC_ADDR_RGB_GREEN:
>>>> +    case EC_ADDR_RGB_BLUE:
>>>> +    case EC_ADDR_BIOS_OEM_2:
>>>> +    case EC_ADDR_KBD_STATUS:
>>>>       case EC_ADDR_OEM_4:
>>>>       case EC_ADDR_CHARGE_CTRL:
>>>>       case EC_ADDR_LIGHTBAR_BAT_CTRL:
>>>> @@ -616,8 +667,10 @@ static bool uniwill_volatile_reg(struct device 
>>>> *dev, unsigned int reg)
>>>>       case EC_ADDR_BIOS_OEM:
>>>>       case EC_ADDR_PWM_1:
>>>>       case EC_ADDR_PWM_2:
>>>> +    case EC_ADDR_SUPPORT_2:
>>>>       case EC_ADDR_TRIGGER:
>>>>       case EC_ADDR_SWITCH_STATUS:
>>>> +    case EC_ADDR_KBD_STATUS:
>>>>       case EC_ADDR_OEM_4:
>>>>       case EC_ADDR_CHARGE_CTRL:
>>>>       case EC_ADDR_USB_C_POWER_PRIORITY:
>>>> @@ -1441,6 +1494,246 @@ static int uniwill_led_init(struct 
>>>> uniwill_data *data)
>>>>                                &init_data);
>>>>   }
>>>>   +static int uniwill_notify_kbd_led(struct uniwill_data *data, int 
>>>> brightness)
>>>> +{
>>>> +    struct led_classdev *led_cdev;
>>>> +    int ret;
>>>> +
>>>> +    if (data->single_color_kbd)
>>>> +        led_cdev = &data->kbd_led_cdev;
>>>> +    else
>>>> +        led_cdev = &data->kbd_led_mc_cdev.led_cdev;
>>>> +
>>>> +    guard(mutex)(&led_cdev->led_access);
>>>> +
>>>> +    /* Sync the LED brightness with the actual hardware state */
>>>> +    ret = led_update_brightness(led_cdev);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    led_classdev_notify_brightness_hw_changed(led_cdev, brightness);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +#define KBD_LED_MASK    (KBD_BRIGHTNESS_MASK | KBD_APPLY | 
>>>> KBD_POWER_OFF)
>>>> +
>>>> +static int uniwill_kbd_led_write_brightness(struct uniwill_data 
>>>> *data, int brightness)
>>>> +{
>>>> +    /* KBD_POWER_OFF is always implicitly cleared */
>>>> +    unsigned int regval = FIELD_PREP(KBD_BRIGHTNESS_MASK, 
>>>> brightness) | KBD_APPLY;
>>>> +
>>>> +    /* We must ensure that the "apply" bit is always written */
>>>> +    return regmap_write_bits(data->regmap, EC_ADDR_KBD_STATUS, 
>>>> KBD_LED_MASK, regval);
>>>> +}
>>>> +
>>>> +static int uniwill_kbd_led_read_brightness(struct uniwill_data *data)
>>>> +{
>>>> +    unsigned int regval;
>>>> +    int ret;
>>>> +
>>>> +    ret = regmap_read(data->regmap, EC_ADDR_KBD_STATUS, &regval);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    return min(FIELD_GET(KBD_BRIGHTNESS_MASK, regval), 
>>>> data->kbd_led_max_brightness);
>>>> +}
>>>> +
>>>> +static int uniwill_kbd_led_brightness_set(struct led_classdev 
>>>> *led_cdev,
>>>> +                      enum led_brightness brightness)
>>>> +{
>>>> +    struct uniwill_data *data = container_of(led_cdev, struct 
>>>> uniwill_data, kbd_led_cdev);
>>>> +
>>>> +    return uniwill_kbd_led_write_brightness(data, brightness);
>>>> +}
>>>> +
>>>> +static enum led_brightness uniwill_kbd_led_brightness_get(struct 
>>>> led_classdev *led_cdev)
>>>> +{
>>>> +    struct uniwill_data *data = container_of(led_cdev, struct 
>>>> uniwill_data, kbd_led_cdev);
>>>> +
>>>> +    return uniwill_kbd_led_read_brightness(data);
>>>> +}
>>>> +
>>>> +static const unsigned int 
>>>> uniwill_kbd_led_channel_to_reg[KBD_LED_CHANNELS] = {
>>>> +    EC_ADDR_RGB_RED,
>>>> +    EC_ADDR_RGB_GREEN,
>>>> +    EC_ADDR_RGB_BLUE,
>>>> +};
>>>> +
>>>> +static int uniwill_kbd_led_mc_brightness_set(struct led_classdev 
>>>> *led_cdev,
>>>> +                         enum led_brightness brightness)
>>>> +{
>>>> +    struct led_classdev_mc *led_mc_cdev = lcdev_to_mccdev(led_cdev);
>>>> +    struct uniwill_data *data = container_of(led_mc_cdev, struct 
>>>> uniwill_data, kbd_led_mc_cdev);
>>>> +    unsigned int min_intensity = 0;
>>>> +    unsigned int regval;
>>>> +    int ret;
>>>> +
>>>> +    guard(mutex)(&data->kbd_rgb_led_lock);
>>>> +
>>>> +    /*
>>>> +     * The EC interprets a RGB value of 0x000000 as a command to 
>>>> restore
>>>> +     * the device-specfic default RGB value. Work around this by 
>>>> writing
>>>> +     * a RGB value of 0x010101 (faint white) instead.
>>>> +     */
>>>> +    if (data->kbd_led_mc_subled_info[0].intensity == 0 &&
>>>> +        data->kbd_led_mc_subled_info[1].intensity == 0 &&
>>>> +        data->kbd_led_mc_subled_info[2].intensity == 0)
>>>> +        min_intensity = 1;
>>>> +
>>>> +    for (int i = 0; i < KBD_LED_CHANNELS; i++) {
>>>> +        regval = max(data->kbd_led_mc_subled_info[i].intensity, 
>>>> min_intensity);
>>>> +        ret = regmap_write(data->regmap, 
>>>> uniwill_kbd_led_channel_to_reg[i], regval);
>>>> +        if (ret < 0)
>>>> +            return ret;
>>>> +    }
>>>> +
>>>> +    ret = regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, 
>>>> RGB_APPLY_COLOR, RGB_APPLY_COLOR);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    return uniwill_kbd_led_write_brightness(data, brightness);
>>>> +}
>>>> +
>>>> +static enum led_brightness 
>>>> uniwill_kbd_led_mc_brightness_get(struct led_classdev *led_cdev)
>>>> +{
>>>> +    struct led_classdev_mc *led_mc_cdev = lcdev_to_mccdev(led_cdev);
>>>> +    struct uniwill_data *data = container_of(led_mc_cdev, struct 
>>>> uniwill_data, kbd_led_mc_cdev);
>>>> +
>>>> +    return uniwill_kbd_led_read_brightness(data);
>>>> +}
>>>> +
>>>> +static int uniwill_kbd_led_init(struct uniwill_data *data)
>>>> +{
>>>> +    unsigned int color_indices[KBD_LED_CHANNELS] = {
>>>> +        LED_COLOR_ID_RED,
>>>> +        LED_COLOR_ID_GREEN,
>>>> +        LED_COLOR_ID_BLUE,
>>>> +    };
>>>> +    struct led_init_data init_data = {
>>>> +        .devicename = DRIVER_NAME,
>>>> +        .devname_mandatory = true,
>>>> +    };
>>>> +    bool intensity_all_zeros = true;
>>>> +    bool needs_trigger = false;
>>>> +    unsigned int regval;
>>>> +    int ret;
>>>> +
>>>> +    if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_KEYBOARD_BACKLIGHT))
>>>> +        return 0;
>>>> +
>>>> +    ret = regmap_read(data->regmap, EC_ADDR_SUPPORT_2, &regval);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    if (!(regval & CHINA_MODE)) {
>>>> +        ret = regmap_set_bits(data->regmap, EC_ADDR_BIOS_OEM_2, 
>>>> ENABLE_CHINA_MODE);
>>>> +        if (ret < 0)
>>>> +            return ret;
>>>> +    }
>>>> +
>>>> +    ret = regmap_read(data->regmap, EC_ADDR_KBD_STATUS, &regval);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    regval |= KBD_APPLY;
>>>> +    regval &= ~KBD_POWER_OFF;
>>>> +    ret = regmap_write(data->regmap, EC_ADDR_KBD_STATUS, regval);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    switch (data->project_id) {
>>>> +    case PROJECT_ID_PF:
>>>> +    case PROJECT_ID_PF4MU_PF4MN_PF5MU:
>>>> +    case PROJECT_ID_PH4TRX1:
>>>> +    case PROJECT_ID_PH4TUX1:
>>>> +    case PROJECT_ID_PH4TQX1:
>>>> +    case PROJECT_ID_PH6TRX1:
>>>> +    case PROJECT_ID_PH6TQXX:
>>>> +    case PROJECT_ID_PHXAXXX:
>>>> +    case PROJECT_ID_PHXPXXX:
>>>> +        data->single_color_kbd = true;
>>>> +        break;
>>>> +    default:
>>>> +        data->single_color_kbd = regval & KBD_WHITE_ONLY;
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    if (data->single_color_kbd) {
>>>> +        init_data.default_label = "white:" 
>>>> LED_FUNCTION_KBD_BACKLIGHT;
>>>> +        data->kbd_led_cdev.max_brightness = 
>>>> data->kbd_led_max_brightness;
>>>> +        data->kbd_led_cdev.color = LED_COLOR_ID_WHITE;
>>>> +        data->kbd_led_cdev.flags = LED_BRIGHT_HW_CHANGED | 
>>>> LED_REJECT_NAME_CONFLICT;
>>>> +        data->kbd_led_cdev.brightness_set_blocking = 
>>>> uniwill_kbd_led_brightness_set;
>>>> +        data->kbd_led_cdev.brightness_get = 
>>>> uniwill_kbd_led_brightness_get;
>>>> +
>>>> +        return devm_led_classdev_register_ext(data->dev, 
>>>> &data->kbd_led_cdev, &init_data);
>>>> +    }
>>>> +
>>>> +    for (int i = 0; i < KBD_LED_CHANNELS; i++) {
>>>> +        data->kbd_led_mc_subled_info[i].color_index = 
>>>> color_indices[i];
>>>> +
>>>> +        ret = regmap_read(data->regmap, 
>>>> uniwill_kbd_led_channel_to_reg[i], &regval);
>>>> +        if (ret < 0)
>>>> +            return ret;
>>>> +
>>>> +        /*
>>>> +         * Make sure that the initial intensity value is not 
>>>> greater than
>>>> +         * the maximum intensity.
>>>> +         */
>>>> +        if (regval > KBD_LED_MAX_INTENSITY) {
>>>> +            regval = KBD_LED_MAX_INTENSITY;
>>>> +            ret = regmap_write(data->regmap, 
>>>> uniwill_kbd_led_channel_to_reg[i], regval);
>>>> +            if (ret < 0)
>>>> +                return ret;
>>>> +
>>>> +            needs_trigger = true;
>>>> +        }
>>>> +
>>>> +        if (regval)
>>>> +            intensity_all_zeros = false;
>>>> +
>>>> +        data->kbd_led_mc_subled_info[i].intensity = regval;
>>>> +        data->kbd_led_mc_subled_info[i].max_intensity = 
>>>> KBD_LED_MAX_INTENSITY;
>>>> +        data->kbd_led_mc_subled_info[i].channel = i;
>>>> +    }
>>>> +
>>>> +    /* See uniwill_kbd_led_mc_brightness_set() for an 
>>>> explaination. */
>>>> +    if (intensity_all_zeros) {
>>>> +        for (int i = 0; i < KBD_LED_CHANNELS; i++) {
>>>> +            data->kbd_led_mc_subled_info[i].intensity = 1;
>>>> +            ret = regmap_write(data->regmap, 
>>>> uniwill_kbd_led_channel_to_reg[i], 1);
>>>> +            if (ret < 0)
>>>> +                return ret;
>>>> +        }
>>>> +
>>>> +        needs_trigger = true;
>>>> +    }
>>>> +
>>>> +    if (needs_trigger) {
>>>> +        ret = regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, 
>>>> RGB_APPLY_COLOR,
>>>> +                    RGB_APPLY_COLOR);
>>>> +        if (ret < 0)
>>>> +            return ret;
>>>> +    }
>>>> +
>>>> +    ret = devm_mutex_init(data->dev, &data->kbd_rgb_led_lock);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    init_data.default_label = "multicolor:" 
>>>> LED_FUNCTION_KBD_BACKLIGHT;
>>>> +    data->kbd_led_mc_cdev.led_cdev.max_brightness = 
>>>> data->kbd_led_max_brightness;
>>>> +    data->kbd_led_mc_cdev.led_cdev.color = LED_COLOR_ID_MULTI;
>>>> +    data->kbd_led_mc_cdev.led_cdev.flags = LED_BRIGHT_HW_CHANGED | 
>>>> LED_REJECT_NAME_CONFLICT;
>>>> + data->kbd_led_mc_cdev.led_cdev.brightness_set_blocking = 
>>>> uniwill_kbd_led_mc_brightness_set;
>>>> +    data->kbd_led_mc_cdev.led_cdev.brightness_get = 
>>>> uniwill_kbd_led_mc_brightness_get;
>>>> +    data->kbd_led_mc_cdev.subled_info = data->kbd_led_mc_subled_info;
>>>> +    data->kbd_led_mc_cdev.num_colors = KBD_LED_CHANNELS;
>>>> +
>>>> +    return devm_led_classdev_multicolor_register_ext(data->dev, 
>>>> &data->kbd_led_mc_cdev,
>>>> +                             &init_data);
>>>> +}
>>>> +
>>>
>>> Sorry I know I'm way to late with my comment, but I'm not at all a 
>>> fan of how complex this whole function is:
>>>
>>> - When we have to set max brightness on a per device basis anyway, 
>>> we can also set rgb or white only there and can skip this whole 
>>> detection mechanism with the supported bits that are unreliable anyway
>>>
>>> - we have a 2nd "supported devices/features list" inside this 
>>> function which is abstracting away from the actual supported feature 
>>> list
>>>
>>> Should i try to spin up something much simpler, but that requires 
>>> explicit setting in the device descriptor?
>>>
>>> Best regards
>>>
>>> Werner
>>>
>> Good point, but in this case i think having this small device list is 
>> OK, because the original control center
>> does something similar.
> yeah the original cc and tuxedo-drivers has these small lists all over 
> the place ... also don't like it there
>> If we encounter a device that does not work with this strategy then 
>> we can indeed
>> move the code into the device descriptor.
>
> later we might not be able to get the information anymore which 
> devices are rgb and which are white only when the supported devices 
> lists grows, then we are stuck to reading the feature bits.
>
> best regards,
>
> Werner
>
Good point, in this case using the device descriptor for this feature detection
might indeed be better in the long term.

Do you want to send the patches or should i do it?

Thanks,
Armin Wolf

>>
>> Thanks,
>> Armin Wolf
>>
>>>>   static unsigned int uniwill_sanitize_battery_threshold(unsigned 
>>>> int value)
>>>>   {
>>>>       /* 0 means "charging threshold not active" */
>>>> @@ -1789,6 +2082,31 @@ static int uniwill_notifier_call(struct 
>>>> notifier_block *nb, unsigned long action
>>>>           sysfs_notify(&data->dev->kobj, NULL, "fn_lock");
>>>>             return NOTIFY_OK;
>>>> +    case UNIWILL_OSD_KB_LED_LEVEL0:
>>>> +        if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_KEYBOARD_BACKLIGHT))
>>>> +            return NOTIFY_DONE;
>>>> +
>>>> +        return notifier_from_errno(uniwill_notify_kbd_led(data, 0));
>>>> +    case UNIWILL_OSD_KB_LED_LEVEL1:
>>>> +        if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_KEYBOARD_BACKLIGHT))
>>>> +            return NOTIFY_DONE;
>>>> +
>>>> +        return notifier_from_errno(uniwill_notify_kbd_led(data, 1));
>>>> +    case UNIWILL_OSD_KB_LED_LEVEL2:
>>>> +        if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_KEYBOARD_BACKLIGHT))
>>>> +            return NOTIFY_DONE;
>>>> +
>>>> +        return notifier_from_errno(uniwill_notify_kbd_led(data, 2));
>>>> +    case UNIWILL_OSD_KB_LED_LEVEL3:
>>>> +        if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_KEYBOARD_BACKLIGHT))
>>>> +            return NOTIFY_DONE;
>>>> +
>>>> +        return notifier_from_errno(uniwill_notify_kbd_led(data, 3));
>>>> +    case UNIWILL_OSD_KB_LED_LEVEL4:
>>>> +        if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_KEYBOARD_BACKLIGHT))
>>>> +            return NOTIFY_DONE;
>>>> +
>>>> +        return notifier_from_errno(uniwill_notify_kbd_led(data, 4));
>>>>       default:
>>>>           mutex_lock(&data->input_lock);
>>>>           sparse_keymap_report_event(data->input_device, action, 1, 
>>>> true);
>>>> @@ -1842,6 +2160,7 @@ static int uniwill_ec_init(struct 
>>>> uniwill_data *data)
>>>>       if (ret < 0)
>>>>           return ret;
>>>>   +    data->project_id = value;
>>>>       dev_dbg(data->dev, "Project ID: %u\n", value);
>>>>         ret = regmap_set_bits(data->regmap, EC_ADDR_AP_OEM, 
>>>> ENABLE_MANUAL_CTRL);
>>>> @@ -1885,6 +2204,7 @@ static int uniwill_probe(struct 
>>>> platform_device *pdev)
>>>>           return ret;
>>>>         data->features = device_descriptor.features;
>>>> +    data->kbd_led_max_brightness = 
>>>> device_descriptor.kbd_led_max_brightness;
>>>>         /*
>>>>        * Some devices might need to perform some device-specific 
>>>> initialization steps
>>>> @@ -1905,6 +2225,10 @@ static int uniwill_probe(struct 
>>>> platform_device *pdev)
>>>>       if (ret < 0)
>>>>           return ret;
>>>>   +    ret = uniwill_kbd_led_init(data);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>>       ret = uniwill_hwmon_init(data);
>>>>       if (ret < 0)
>>>>           return ret;
>>>> @@ -1976,6 +2300,31 @@ static int uniwill_suspend_battery(struct 
>>>> uniwill_data *data)
>>>>       return regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, 
>>>> &data->last_charge_ctrl);
>>>>   }
>>>>   +static int uniwill_suspend_kbd_led(struct uniwill_data *data)
>>>> +{
>>>> +    unsigned int regval;
>>>> +    int ret;
>>>> +
>>>> +    if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_KEYBOARD_BACKLIGHT))
>>>> +        return 0;
>>>> +
>>>> +    ret = regmap_read(data->regmap, EC_ADDR_KBD_STATUS, &regval);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    /*
>>>> +     * Save the current keyboard backlight settings in order to 
>>>> restore them
>>>> +     * during resume. We cannot use the regmap code for that since 
>>>> this register
>>>> +     * needs to be declared as volatile because the brightness can 
>>>> be changed
>>>> +     * by the EC.
>>>> +     */
>>>> +    data->last_kbd_status = regval;
>>>> +    FIELD_MODIFY(KBD_BRIGHTNESS_MASK, &regval, 0);
>>>> +    regval |= KBD_APPLY | KBD_POWER_OFF;
>>>> +
>>>> +    return regmap_write(data->regmap, EC_ADDR_KBD_STATUS, regval);
>>>> +}
>>>> +
>>>>   static int uniwill_suspend_nvidia_ctgp(struct uniwill_data *data)
>>>>   {
>>>>       if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL))
>>>> @@ -2006,6 +2355,10 @@ static int uniwill_suspend(struct device *dev)
>>>>       if (ret < 0)
>>>>           return ret;
>>>>   +    ret = uniwill_suspend_kbd_led(data);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>>       ret = uniwill_suspend_nvidia_ctgp(data);
>>>>       if (ret < 0)
>>>>           return ret;
>>>> @@ -2052,6 +2405,23 @@ static int uniwill_resume_battery(struct 
>>>> uniwill_data *data)
>>>>       return 0;
>>>>   }
>>>>   +static int uniwill_resume_kbd_led(struct uniwill_data *data)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_KEYBOARD_BACKLIGHT))
>>>> +        return 0;
>>>> +
>>>> +    ret = regmap_write(data->regmap, EC_ADDR_KBD_STATUS, 
>>>> data->last_kbd_status | KBD_APPLY);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    if (data->single_color_kbd)
>>>> +        return 0;
>>>> +
>>>> +    return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, 
>>>> RGB_APPLY_COLOR, RGB_APPLY_COLOR);
>>>> +}
>>>> +
>>>>   static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data)
>>>>   {
>>>>       if (!uniwill_device_supports(data, 
>>>> UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL))
>>>> @@ -2096,6 +2466,10 @@ static int uniwill_resume(struct device *dev)
>>>>       if (ret < 0)
>>>>           return ret;
>>>>   +    ret = uniwill_resume_kbd_led(data);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>>       ret = uniwill_resume_nvidia_ctgp(data);
>>>>       if (ret < 0)
>>>>           return ret;
>>>> @@ -2745,6 +3119,8 @@ static int __init uniwill_init(void)
>>>>       if (force) {
>>>>           /* Assume that the device supports all features except 
>>>> the charge limit */
>>>>           device_descriptor.features = UINT_MAX & 
>>>> ~UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT;
>>>> +        /* Some models only support 3 brightness levels */
>>>> +        device_descriptor.kbd_led_max_brightness = 4;
>>>>           pr_warn("Enabling potentially unsupported features\n");
>>>>       }
>>>
>

  reply	other threads:[~2026-07-09  5:50 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-30 17:08 [PATCH 0/7] platform/x86: uniwill-laptop: Support additional features Armin Wolf
2026-05-30 17:08 ` [PATCH 1/7] platform/x86: uniwill-laptop: Add keyboard backlight support Armin Wolf
2026-07-08 19:11   ` Werner Sembach
2026-07-08 20:08     ` Armin Wolf
2026-07-08 20:15       ` Werner Sembach
2026-07-09  5:49         ` Armin Wolf [this message]
2026-07-09 13:23           ` Werner Sembach
2026-07-09 20:23             ` Armin Wolf
2026-05-30 17:08 ` [PATCH 2/7] platform/x86: uniwill-laptop: Handle screen-related events Armin Wolf
2026-05-30 17:08 ` [PATCH 3/7] platform/x86: uniwill-laptop: Add AC auto boot support Armin Wolf
2026-05-30 17:08 ` [PATCH 4/7] platform/x86: uniwill-laptop: Add support for USB powershare Armin Wolf
2026-05-30 17:08 ` [PATCH 5/7] platform/x86: uniwill-laptop: Add support for the MACHENIKE L16 Pro Armin Wolf
2026-05-30 17:08 ` [PATCH 6/7] platform/x86: uniwill-laptop: Add support for the AiStone X4SP4NAL Armin Wolf
2026-05-30 17:08 ` [PATCH 7/7] platform/x86: uniwill-laptop: Add lightbar support for LAPQC71A/B Armin Wolf
2026-07-08 20:08   ` Werner Sembach
2026-07-09  8:57     ` Ilpo Järvinen
2026-07-09 13:13       ` Werner Sembach
2026-07-09 13:45         ` Ilpo Järvinen
2026-07-09 14:06           ` Werner Sembach
2026-06-10 10:50 ` [PATCH 0/7] platform/x86: uniwill-laptop: Support additional features Ilpo Järvinen
2026-06-10 16:36   ` Armin Wolf
2026-06-11 13:21     ` Ilpo Järvinen
2026-06-11 15:00       ` Armin Wolf
2026-07-01 11:30 ` Ilpo Järvinen
2026-07-02 17:21   ` Armin Wolf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a681b59d-1719-4a8e-9b75-e8317c899b27@gmx.de \
    --to=w_armin@gmx.de \
    --cc=hansg@kernel.org \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=platform-driver-x86@vger.kernel.org \
    --cc=wse@tuxedocomputers.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.