mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: Zane <git@turtledev.nz>
Cc: "Corentin Chary" <corentin.chary@gmail.com>,
	"Luke D. Jones" <luke@ljones.dev>,
	"Denis Benato" <denis.benato@linux.dev>,
	"Hans de Goede" <hansg@kernel.org>,
	platform-driver-x86@vger.kernel.org,
	"José Guilherme de Castro Rodrigues"
	<jose.guilherme.cr.bh@gmail.com>,
	LKML <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v3] platform/x86: asus-wmi: fix camera key LED on Zenbook S14
Date: Mon, 19 Oct 2026 09:05:07 +0300 (EEST)	[thread overview]
Message-ID: <cd44cbf7-6a4a-c993-fe96-7cc913bd0608@linux.intel.com> (raw)
In-Reply-To: <20261017-asus-wmi-camera-led-v3-1-845a82373997@turtledev.nz>

[-- Attachment #1: Type: text/plain, Size: 9359 bytes --]

On Sat, 17 Oct 2026, Zane via B4 Relay wrote:

> From: Zane <git@turtledev.nz>
> 
> On some newer Zenbooks (Zenbook S 14 UX5406SA, Zenbook S 16 UM5606WA) the
> camera key turns the camera off in firmware, but the LED on the key never
> lights up under Linux. These machines don't have
> ASUS_WMI_DEVID_CAMERA_LED, so asus-wmi never registers asus::camera. The
> firmware exposes the state through ASUS_WMI_DEVID_CAMERA_LED_NEG instead.
> 
> Reading CAMERA_LED_NEG is what drives the LED: the DSTS handler reads the
> camera state from the EC and writes it to the LED GPIO before returning
> it. The set side can't be used, the firmware only accepts DEVS on this
> device once per boot.
> 
> So read it at probe, which fixes the LED being dark after a cold boot
> with the camera disabled, and after every camera key event. Also
> register a read-only asus::camera LED on these machines so userspace can
> see the camera state and gets a brightness_hw_changed event on each key
> press.
> 
> This builds on José Guilherme de Castro Rodrigues' v2, which added the
> read after the camera key event.

You should retain his Signed-off-by tag if that's the case + add 
Co-developed-by tag.

> Link: https://lore.kernel.org/r/adg6GzkykThAB_4u@djouze-zen
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221752
> Assisted-by: Claude:claude-opus-5-5 iasl
> Signed-off-by: Zane <git@turtledev.nz>

Please see what is required when you're signing off something:

Documentation/process/submitting-patches.rst

Also, you need to use your real name, not some nickname (basically what 
you'd use to sign a paper document/contract/etc.

> ---
> José, this is a respin of your v2 since it had stalled, I hope that's ok.
> Happy to drop it if you'd rather send v3 yourself, or to add your
> Co-developed-by/Signed-off-by if you want them on it.
> 
> Changes in v3:
> - Rebased on pdx86 for-next
> - Also read CAMERA_LED_NEG at probe, so the LED is right after a cold boot
> - Register a read-only asus::camera LED when only CAMERA_LED_NEG exists,
>   with brightness_hw_changed on key presses (Ilpo's suggestion on v2)
> - Comment on why the read is what updates the LED (also from v2 review)
> 
> Tested on a Zenbook S 14 UX5406SA, BIOS 309, on the Fedora 7.2.9 kernel
> with the same change applied to its asus-wmi:
> - Key presses: the LED, asus::camera and the camera output (live picture
>   vs. the firmware's grey "camera disabled" frame) switch together every
>   time, and brightness_hw_changed follows.
> - Cold boot with the camera disabled: the LED is lit and asus::camera
>   reads 1 before anything is pressed. Without the patch the LED stays dark
>   until the next key press.
> - Suspend/resume: the LEDs come back correctly, but that's the firmware
>   restoring them, it does the same without the patch. So nothing is done
>   on resume.
> 
> A Zenbook S 16 UM5606WA owner reported on the bug that reading
> CAMERA_LED_NEG after the key event (WMI code 0x85 there) fixes it on that
> machine too. The DSDT details are in the bugzilla entry.
> 
> I used Claude (Anthropic's AI assistant) while working on this. It helped
> me read through the DSDT, drafted the changes on top of v2 and wrote the
> test scripts. I went through the patch myself and ran the tests on my
> laptop.
> 
> v2: https://lore.kernel.org/r/adg6GzkykThAB_4u@djouze-zen
> ---
>  drivers/platform/x86/asus-wmi.c            | 72 ++++++++++++++++++++++++++++++
>  include/linux/platform_data/x86/asus-wmi.h |  2 +-
>  2 files changed, 73 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index fb1d4a0..fd2ba8c 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -72,6 +72,8 @@ module_param(fnlock_default, bool, 0444);
>  #define NOTIFY_KBD_TTP			0xae
>  #define NOTIFY_LID_FLIP			0xfa
>  #define NOTIFY_LID_FLIP_ROG		0xbd
> +#define NOTIFY_CAMERA_TOGGLE_1		0x82
> +#define NOTIFY_CAMERA_TOGGLE_2		0x85
>  #define NOTIFY_KEYSTONE			0xb4
>  
>  #define ASUS_WMI_FNLOCK_BIOS_DISABLED	BIT(0)
> @@ -312,6 +314,7 @@ struct asus_wmi {
>  	u32 kbd_rgb_dev;
>  	bool kbd_rgb_state_available;
>  	bool oobe_state_available;
> +	bool camera_neg_led_available;
>  
>  	u8 throttle_thermal_policy_mode;
>  	u32 throttle_thermal_policy_dev;
> @@ -2096,6 +2099,53 @@ static int camera_led_set(struct led_classdev *led_cdev,
>  	return err < 0 ? err : 0;
>  }
>  
> +static bool is_camera_toggle(int code)
> +{
> +	return code == NOTIFY_CAMERA_TOGGLE_1 || code == NOTIFY_CAMERA_TOGGLE_2;
> +}
> +
> +/*
> + * Some models (e.g. Zenbook S 14 UX5406SA, Zenbook S 16 UM5606WA) have no
> + * ASUS_WMI_DEVID_CAMERA_LED. The firmware handles the camera key itself and
> + * only exposes the state through ASUS_WMI_DEVID_CAMERA_LED_NEG, and reading
> + * that has a side effect: the firmware copies the state to the LED GPIO.
> + * Nothing else updates the LED, so read it at probe and after every camera
> + * key event. Setting it is no use, the firmware accepts DEVS on this device
> + * only once per boot.
> + *
> + * Returns 1 if the camera is disabled (LED lit), 0 if enabled, or -errno.
> + */
> +static int camera_neg_led_sync(struct asus_wmi *asus)
> +{
> +	u32 result;
> +	int err;
> +
> +	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CAMERA_LED_NEG, &result);
> +	if (err)
> +		return err;
> +
> +	return !(result & ASUS_WMI_DSTS_STATUS_BIT);
> +}
> +
> +static enum led_brightness camera_neg_led_get(struct led_classdev *led_cdev)
> +{
> +	struct asus_wmi *asus = container_of(led_cdev, struct asus_wmi, camera_led);

Missing include (pre-existing problem but lets finally fix it).

> +	int state = camera_neg_led_sync(asus);
> +
> +	return state < 0 ? LED_OFF : state;
> +}
> +
> +/* The firmware owns this LED; a write just resyncs it with the camera state. */
> +static int camera_neg_led_set(struct led_classdev *led_cdev,
> +			      enum led_brightness brightness)
> +{
> +	struct asus_wmi *asus = container_of(led_cdev, struct asus_wmi, camera_led);
> +
> +	if (!(led_cdev->flags & LED_UNREGISTERING))
> +		camera_neg_led_sync(asus);
> +	return 0;
> +}
> +
>  static void asus_wmi_led_exit(struct asus_wmi *asus)
>  {
>  	scoped_guard(spinlock_irqsave, &asus_ref.lock)
> @@ -2233,6 +2283,17 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
>  		asus->camera_led.brightness_get = camera_led_get;
>  		asus->camera_led.brightness_set_blocking = camera_led_set;
>  
> +		rv = led_classdev_register(&asus->platform_device->dev,
> +						&asus->camera_led);
> +		if (rv)
> +			goto error;
> +	} else if (asus->camera_neg_led_available) {
> +		asus->camera_led.name = "asus::camera";
> +		asus->camera_led.max_brightness = 1;
> +		asus->camera_led.flags = LED_BRIGHT_HW_CHANGED;
> +		asus->camera_led.brightness_get = camera_neg_led_get;
> +		asus->camera_led.brightness_set_blocking = camera_neg_led_set;
> +
>  		rv = led_classdev_register(&asus->platform_device->dev,
>  						&asus->camera_led);

This duplicates code so you should mostly reuse the existing block, and 
only branch where you really need something different (the handlers + 
flags).

>  		if (rv)
> @@ -4733,6 +4794,13 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
>  	if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
>  		return;
>  
> +	if (is_camera_toggle(code) && asus->camera_neg_led_available) {
> +		int state = camera_neg_led_sync(asus);
> +
> +		if (state >= 0 && (asus->camera_led.flags & LED_BRIGHT_HW_CHANGED))

And why is the second condition necessary??

> +			led_classdev_notify_brightness_hw_changed(&asus->camera_led, state);
> +	}
> +
>  	if (!sparse_keymap_report_event(asus->inputdev, code,
>  					key_value, autorelease))
>  		pr_info("Unknown key code 0x%x\n", code);
> @@ -5210,6 +5278,10 @@ static int asus_wmi_add(struct platform_device *pdev)
>  #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
>  
>  	asus->oobe_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_OOBE);
> +	asus->camera_neg_led_available =
> +		asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CAMERA_LED_NEG);
> +	if (asus->camera_neg_led_available)
> +		camera_neg_led_sync(asus);
>  
>  	if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
>  		asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY;
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index 4ea1ccc..5964063 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -57,7 +57,7 @@
>  #define ASUS_WMI_DEVID_MUTE_LED	0x0004001C
>  
>  /* Disable Camera LED */
> -#define ASUS_WMI_DEVID_CAMERA_LED_NEG	0x00060078 /* 0 = on (unused) */
> +#define ASUS_WMI_DEVID_CAMERA_LED_NEG	0x00060078 /* 0 = on */
>  #define ASUS_WMI_DEVID_CAMERA_LED	0x00060079 /* 1 = on */
>  
>  /* Backlight and Brightness */
> 
> ---
> base-commit: ea40f1e9f07aab937b9a217633be7a1f66260394
> change-id: 20261017-asus-wmi-camera-led-06a6e05f01e2
> 
> Best regards,
> --  
> Zane <git@turtledev.nz>
> 
> 

-- 
 i.

      reply	other threads:[~2026-10-19  6:05 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-16 21:50 Zane via B4 Relay
2026-10-19  6:05 ` Ilpo Järvinen [this message]

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=cd44cbf7-6a4a-c993-fe96-7cc913bd0608@linux.intel.com \
    --to=ilpo.jarvinen@linux.intel.com \
    --cc=corentin.chary@gmail.com \
    --cc=denis.benato@linux.dev \
    --cc=git@turtledev.nz \
    --cc=hansg@kernel.org \
    --cc=jose.guilherme.cr.bh@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luke@ljones.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®