From: "Luke D. Jones" <luke@ljones.dev>
To: Antheas Kapenekakis <lkml@antheas.dev>,
platform-driver-x86@vger.kernel.org, linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, "Jiri Kosina" <jikos@kernel.org>,
"Benjamin Tissoires" <bentiss@kernel.org>,
"Corentin Chary" <corentin.chary@gmail.com>,
"Hans de Goede" <hdegoede@redhat.com>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Subject: Re: [PATCH v3 09/10] HID: asus: add basic RGB support
Date: Sun, 23 Mar 2025 19:40:34 +1300 [thread overview]
Message-ID: <bb3071d4-7910-46c2-ab48-aed574bcd758@ljones.dev> (raw)
In-Reply-To: <20250322102804.418000-10-lkml@antheas.dev>
On 22/03/25 23:28, Antheas Kapenekakis wrote:
> Adds basic RGB support to hid-asus through multi-index. The interface
> works quite well, but has not gone through much stability testing.
> Applied on demand, if userspace does not touch the RGB sysfs, not
> even initialization is done. Ensuring compatibility with existing
> userspace programs.
>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
> drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++----
> 1 file changed, 155 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 905453a4eb5b7..9d8ccfde5912e 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -30,6 +30,7 @@
> #include <linux/input/mt.h>
> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
> #include <linux/power_supply.h>
> +#include <linux/led-class-multicolor.h>
> #include <linux/leds.h>
>
> #include "hid-ids.h"
> @@ -85,6 +86,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> #define QUIRK_ROG_NKEY_KEYBOARD BIT(11)
> #define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
> #define QUIRK_HANDLE_GENERIC BIT(13)
> +#define QUIRK_ROG_NKEY_RGB BIT(14)
>
> #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
> QUIRK_NO_INIT_REPORTS | \
> @@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
>
> struct asus_kbd_leds {
> struct asus_hid_listener listener;
> + struct led_classdev_mc mc_led;
> + struct mc_subled subled_info[3];
> struct hid_device *hdev;
> struct work_struct work;
> unsigned int brightness;
> + uint8_t rgb_colors[3];
> + bool rgb_init;
> + bool rgb_set;
> + bool rgb_registered;
> spinlock_t lock;
> bool removed;
> };
> @@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)
> spin_unlock_irqrestore(&led->lock, flags);
> }
>
> -static void asus_kbd_backlight_set(struct asus_hid_listener *listener,
> +static void do_asus_kbd_backlight_set(struct asus_kbd_leds *led, int brightness)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&led->lock, flags);
> + led->brightness = brightness;
> + spin_unlock_irqrestore(&led->lock, flags);
> +
> + asus_schedule_work(led);
> +}
> +
> +static void asus_kbd_listener_set(struct asus_hid_listener *listener,
> int brightness)
> {
> struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds,
> listener);
> + do_asus_kbd_backlight_set(led, brightness);
> + if (led->rgb_registered) {
> + led->mc_led.led_cdev.brightness = brightness;
> + led_classdev_notify_brightness_hw_changed(&led->mc_led.led_cdev,
> + brightness);
> + }
> +}
> +
> +static void asus_kbd_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
> + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
> + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds,
> + mc_led);
> unsigned long flags;
>
> spin_lock_irqsave(&led->lock, flags);
> - led->brightness = brightness;
> + led->rgb_colors[0] = mc_cdev->subled_info[0].intensity;
> + led->rgb_colors[1] = mc_cdev->subled_info[1].intensity;
> + led->rgb_colors[2] = mc_cdev->subled_info[2].intensity;
> + led->rgb_set = true;
> spin_unlock_irqrestore(&led->lock, flags);
>
> - asus_schedule_work(led);
> + do_asus_kbd_backlight_set(led, brightness);
> +}
> +
> +static enum led_brightness asus_kbd_brightness_get(struct led_classdev *led_cdev)
> +{
> + struct led_classdev_mc *mc_led;
> + struct asus_kbd_leds *led;
> + enum led_brightness brightness;
> + unsigned long flags;
> +
> + mc_led = lcdev_to_mccdev(led_cdev);
> + led = container_of(mc_led, struct asus_kbd_leds, mc_led);
> +
> + spin_lock_irqsave(&led->lock, flags);
> + brightness = led->brightness;
> + spin_unlock_irqrestore(&led->lock, flags);
> +
> + return brightness;
> }
>
> -static void asus_kbd_backlight_work(struct work_struct *work)
> +static void asus_kbd_backlight_work(struct asus_kbd_leds *led)
> {
> - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
> u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
> int ret;
> unsigned long flags;
> @@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)
> hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
> }
>
> +static void asus_kbd_rgb_work(struct asus_kbd_leds *led)
> +{
> + u8 rgb_buf[][7] = {
> + { FEATURE_KBD_LED_REPORT_ID1, 0xB3 }, /* set mode */
> + { FEATURE_KBD_LED_REPORT_ID1, 0xB5 }, /* apply mode */
> + { FEATURE_KBD_LED_REPORT_ID1, 0xB4 }, /* save to mem */
> + };
> + unsigned long flags;
> + uint8_t colors[3];
> + bool rgb_init, rgb_set;
> + int ret;
> +
> + spin_lock_irqsave(&led->lock, flags);
> + rgb_init = led->rgb_init;
> + rgb_set = led->rgb_set;
> + led->rgb_set = false;
> + colors[0] = led->rgb_colors[0];
> + colors[1] = led->rgb_colors[1];
> + colors[2] = led->rgb_colors[2];
> + spin_unlock_irqrestore(&led->lock, flags);
> +
> + if (!rgb_set)
> + return;
> +
> + if (rgb_init) {
> + ret = asus_kbd_init(led->hdev, FEATURE_KBD_LED_REPORT_ID1);
> + if (ret < 0) {
> + hid_err(led->hdev, "Asus failed to init RGB: %d\n", ret);
> + return;
> + }
> + spin_lock_irqsave(&led->lock, flags);
> + led->rgb_init = false;
> + spin_unlock_irqrestore(&led->lock, flags);
> + }
> +
> + /* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */
> + rgb_buf[0][4] = colors[0];
> + rgb_buf[0][5] = colors[1];
> + rgb_buf[0][6] = colors[2];
> +
> + for (size_t i = 0; i < ARRAY_SIZE(rgb_buf); i++) {
> + ret = asus_kbd_set_report(led->hdev, rgb_buf[i], sizeof(rgb_buf[i]));
> + if (ret < 0) {
> + hid_err(led->hdev, "Asus failed to set RGB: %d\n", ret);
> + return;
> + }
> + }
> +}
> +
> +static void asus_kbd_work(struct work_struct *work)
> +{
> + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds,
> + work);
> + asus_kbd_backlight_work(led);
> + asus_kbd_rgb_work(led);
> +}
> +
> static int asus_kbd_register_leds(struct hid_device *hdev)
> {
> struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
> unsigned char kbd_func;
> + struct asus_kbd_leds *leds;
> + bool no_led;
> int ret;
>
> ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
> @@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
> if (!drvdata->kbd_backlight)
> return -ENOMEM;
>
> - drvdata->kbd_backlight->removed = false;
> - drvdata->kbd_backlight->brightness = 0;
> - drvdata->kbd_backlight->hdev = hdev;
> - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set;
> - INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
> + leds = drvdata->kbd_backlight;
> + leds->removed = false;
> + leds->brightness = 3;
> + leds->hdev = hdev;
> + leds->listener.brightness_set = asus_kbd_listener_set;
> +
> + leds->rgb_colors[0] = 0;
> + leds->rgb_colors[1] = 0;
> + leds->rgb_colors[2] = 0;
> + leds->rgb_init = true;
> + leds->rgb_set = false;
> + leds->mc_led.led_cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
> + "asus-%s-led",
> + strlen(hdev->uniq) ?
> + hdev->uniq : dev_name(&hdev->dev));
A quick note. This breaks convention for LED names. The style guide is
at Documentation/leds/leds-class.rst. Per my parallel email to this one
I would like to see the mentioned naming scheme `asus:rgb:kbd_backlight`
adopted. Expanding further on one of the points there you might need to
move the led_classdev_mc in to asus-wmi to fulfil having the single
sysfs endpoint. Since you're using the listner pattern it shouldn't be
much work.
> + leds->mc_led.led_cdev.flags = LED_BRIGHT_HW_CHANGED;
> + leds->mc_led.led_cdev.max_brightness = 3,
> + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set,
> + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get,
> + leds->mc_led.subled_info = leds->subled_info,
> + leds->mc_led.num_colors = ARRAY_SIZE(leds->subled_info),
> + leds->subled_info[0].color_index = LED_COLOR_ID_RED;
> + leds->subled_info[1].color_index = LED_COLOR_ID_GREEN;
> + leds->subled_info[2].color_index = LED_COLOR_ID_BLUE;
> +
> + INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_work);
> spin_lock_init(&drvdata->kbd_backlight->lock);
>
> ret = asus_hid_register_listener(&drvdata->kbd_backlight->listener);
> + no_led = !!ret;
> +
> + if (drvdata->quirks & QUIRK_ROG_NKEY_RGB) {
> + ret = devm_led_classdev_multicolor_register(
> + &hdev->dev, &leds->mc_led);
> + if (!ret)
> + leds->rgb_registered = true;
> + no_led &= !!ret;
> + }
>
> - if (ret < 0) {
> + if (no_led) {
> /* No need to have this still around */
> devm_kfree(&hdev->dev, drvdata->kbd_backlight);
> }
>
> - return ret;
> + return no_led ? -ENODEV : 0;
> }
>
> /*
> @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = {
> QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
> { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
> USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR),
> - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
> + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB },
> { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
> USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY),
> QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
> @@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {
> */
> { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
> USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO),
> - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
> + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB },
> { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
> USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) },
> { }
next prev parent reply other threads:[~2025-03-23 6:40 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-22 10:27 [PATCH v3 00/10] HID: asus: Add RGB Support to Asus Z13, Ally, unify backlight asus-wmi, and Z13 QOL Antheas Kapenekakis
2025-03-22 10:27 ` [PATCH v3 01/10] HID: asus: refactor init sequence per spec Antheas Kapenekakis
2025-03-22 22:01 ` Luke D. Jones
2025-03-22 23:05 ` Antheas Kapenekakis
2025-03-22 23:28 ` Luke D. Jones
2025-03-22 23:53 ` Antheas Kapenekakis
2025-03-22 23:54 ` Antheas Kapenekakis
2025-03-23 0:10 ` Luke D. Jones
2025-03-23 0:14 ` Luke D. Jones
2025-03-22 10:27 ` [PATCH v3 02/10] HID: asus: prevent binding to all HID devices on ROG Antheas Kapenekakis
2025-03-22 23:21 ` Luke D. Jones
2025-03-22 10:27 ` [PATCH v3 03/10] HID: Asus: add Z13 folio to generic group for multitouch to work Antheas Kapenekakis
2025-03-22 23:30 ` Luke D. Jones
2025-03-22 23:32 ` Antheas Kapenekakis
2025-03-22 10:27 ` [PATCH v3 04/10] platform/x86: asus-wmi: Add support for multiple kbd RGB handlers Antheas Kapenekakis
2025-03-23 0:02 ` Luke D. Jones
2025-03-24 11:31 ` Hans de Goede
2025-03-24 12:29 ` Antheas Kapenekakis
2025-03-24 12:37 ` Hans de Goede
2025-03-22 10:27 ` [PATCH v3 05/10] HID: asus: listen to the asus-wmi brightness device instead of creating one Antheas Kapenekakis
2025-03-23 6:40 ` Luke D. Jones
2025-03-22 10:28 ` [PATCH v3 06/10] platform/x86: asus-wmi: remove unused keyboard backlight quirk Antheas Kapenekakis
2025-03-23 0:05 ` Luke D. Jones
2025-03-22 10:28 ` [PATCH v3 07/10] platform/x86: asus-wmi: add keyboard brightness event handler Antheas Kapenekakis
2025-03-23 0:06 ` Luke D. Jones
2025-03-22 10:28 ` [PATCH v3 08/10] HID: asus: add support for the asus-wmi brightness handler Antheas Kapenekakis
2025-03-23 0:08 ` Luke D. Jones
2025-03-22 10:28 ` [PATCH v3 09/10] HID: asus: add basic RGB support Antheas Kapenekakis
2025-03-23 6:21 ` Luke D. Jones
2025-03-23 6:40 ` Luke D. Jones [this message]
2025-03-23 11:37 ` Antheas Kapenekakis
2025-03-23 14:45 ` Antheas Kapenekakis
2025-03-23 20:13 ` Luke D. Jones
2025-03-23 20:39 ` Luke D. Jones
2025-03-23 21:08 ` Antheas Kapenekakis
2025-03-22 10:28 ` [PATCH v3 10/10] HID: asus: add RGB support to the ROG Ally units Antheas Kapenekakis
2025-03-23 6:29 ` Luke D. Jones
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=bb3071d4-7910-46c2-ab48-aed574bcd758@ljones.dev \
--to=luke@ljones.dev \
--cc=bentiss@kernel.org \
--cc=corentin.chary@gmail.com \
--cc=hdegoede@redhat.com \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=jikos@kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lkml@antheas.dev \
--cc=platform-driver-x86@vger.kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®