Dear Armin, CE6C0974-0407-4F50-88BA-4FC3B6559AD8 is a WMI interface method. From Lenovo keyboard WMI specification, I found it applicable to LNB products, i.e. YOGA/XiaoXin/Gaming/ThinkBook etc and performs the Lenovo customized hotkeys function for Consumer and SMB notebooks. I implemented the audio mute LED and mic mute LED function of IdeaPad notebook in ideapad_laptop driver, because I found the mute LED function of Thinkpad notebook is implemented by thinkpad_acpi. And ideapad_laptop driver has the similar function as thinkpad_acpi. Thanks, Jackie Dong -----Original Message----- From: Armin Wolf <W_Armin@xxxxxx> Sent: Monday, December 23, 2024 6:34 AM To: Jackie Dong <xy-jackie@xxxxxxx>; ike.pan@xxxxxxxxxxxxx; hdegoede@xxxxxxxxxx; ilpo.jarvinen@xxxxxxxxxxxxxxx; perex@xxxxxxxx; tiwai@xxxxxxxx; bo.liu@xxxxxxxxxxxxxx; kovalev@xxxxxxxxxxxx; me@xxxxxxxxxxx; jaroslaw.janik@xxxxxxxxx; cs@xxxxxxxxx; songxiebing@xxxxxxxxxx; kailang@xxxxxxxxxxx; sbinding@xxxxxxxxxxxxxxxxxxxxx; simont@xxxxxxxxxxxxxxxxxxxxx; josh@xxxxxxxxxxxxxxxxx; rf@xxxxxxxxxxxxxxxxxxxxx Cc: linux-kernel@xxxxxxxxxxxxxxx; platform-driver-x86@xxxxxxxxxxxxxxx; linux-sound@xxxxxxxxxxxxxxx; mpearson-lenovo@xxxxxxxxx; waterflowdeg@xxxxxxxxx; Jackie EG1 Dong <dongeg1@xxxxxxxxxx> Subject: [External] Re: [PATCH 1/2] platform/x86: ideapad-laptop: Support for mic and audio leds. Am 19.12.24 um 11:15 schrieb Jackie Dong: > Implement Lenovo utility data WMI calls needed to make LEDs work on > Ideapads that support this GUID. > This enables the mic and audio LEDs to be updated correctly. > > Tested on below samples. > ThinkBook 13X Gen4 IMH > ThinkBook 14 G6 ABP > ThinkBook 16p Gen4-21J8 > ThinkBook 16p Gen8-IRL > ThinkBook 16p G7+ ASP Hi, i am a bit confused regarding the role of the CE6C0974-0407-4F50-88BA-4FC3B6559AD8 WMI device: - is it a event or a method block? - is it in some way connected with the remaining WMI devices supported by the ideapad-laptop driver? Looking at the code it seems to me that the CE6C0974-0407-4F50-88BA-4FC3B6559AD8 WMI device is not a event device and is not directly connected with the remaining WMI devices (please correct me if i am wrong). Can you please write a separate driver for this WMI device? Getting the ideapad-laptop driver involved in this seems to be unreasonable since the audio led functionality does not interact with the remaining driver. This might be helpful: https://docs.kernel.org/wmi/driver-development-guide.html. > > Suggested-by: Mark Pearson <mpearson-lenovo@xxxxxxxxx> > Signed-off-by: Jackie Dong <xy-jackie@xxxxxxx> > Signed-off-by: Jackie Dong <dongeg1@xxxxxxxxxx> Please keep only the Signed-of tag with the email address used for sending this patch. Besides that its always nice to see vendors getting involved with upstream :). Thanks, Armin Wolf > --- > drivers/platform/x86/ideapad-laptop.c | 157 +++++++++++++++++++++++++- > 1 file changed, 156 insertions(+), 1 deletion(-) > > diff --git a/drivers/platform/x86/ideapad-laptop.c > b/drivers/platform/x86/ideapad-laptop.c > index c64dfc56651d..acea4aa8eac3 100644 > --- a/drivers/platform/x86/ideapad-laptop.c > +++ b/drivers/platform/x86/ideapad-laptop.c > @@ -32,6 +32,7 @@ > #include <linux/sysfs.h> > #include <linux/types.h> > #include <linux/wmi.h> > +#include <sound/control.h> > #include "ideapad-laptop.h" > > #include <acpi/video.h> > @@ -1298,6 +1299,15 @@ static const struct key_entry ideapad_keymap[] = { > { KE_END }, > }; > > +/* > + * Input parameters to mute/unmute audio LED and Mic LED */ struct > +wmi_led_args { > + u8 ID; > + u8 SubID; > + u16 Value; > +}; > + > static int ideapad_input_init(struct ideapad_private *priv) > { > struct input_dev *inputdev; > @@ -2023,15 +2033,145 @@ static void ideapad_check_features(struct ideapad_private *priv) > /* > * WMI driver > */ > +#define IDEAPAD_ACPI_LED_MAX (((SNDRV_CTL_ELEM_ACCESS_MIC_LED -\ > + SNDRV_CTL_ELEM_ACCESS_SPK_LED) >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) > ++ 1) > + > enum ideapad_wmi_event_type { > IDEAPAD_WMI_EVENT_ESC, > IDEAPAD_WMI_EVENT_FN_KEYS, > + IDEAPAD_WMI_EVENT_LUD_KEYS, > }; > > +#define WMI_LUD_GET_SUPPORT 1 > +#define WMI_LUD_SET_FEATURE 2 > + > +#define WMI_LUD_GET_MICMUTE_LED_VER 20 > +#define WMI_LUD_GET_AUDIOMUTE_LED_VER 26 > + > +#define WMI_LUD_SUPPORT_MICMUTE_LED_VER 25 > +#define WMI_LUD_SUPPORT_AUDIOMUTE_LED_VER 27 > + > struct ideapad_wmi_private { > enum ideapad_wmi_event_type event; > + struct led_classdev cdev[IDEAPAD_ACPI_LED_MAX]; > }; > > +static struct wmi_device *led_wdev; > + > +enum mute_led_type { > + MIC_MUTE, > + AUDIO_MUTE, > +}; > + > +static int ideapad_wmi_mute_led_set(enum mute_led_type led_type, struct led_classdev *led_cdev, > + enum led_brightness brightness) > + > +{ > + struct wmi_led_args led_arg = {0, 0, 0}; > + struct acpi_buffer input; > + acpi_status status; > + > + if (led_type == MIC_MUTE) > + led_arg.ID = brightness == LED_ON ? 1 : 2; > + else if (led_type == AUDIO_MUTE) > + led_arg.ID = brightness == LED_ON ? 4 : 5; > + else > + return -EINVAL; > + > + input.length = sizeof(struct wmi_led_args); > + input.pointer = &led_arg; > + status = wmidev_evaluate_method(led_wdev, 0, WMI_LUD_SET_FEATURE, > +&input, NULL); > + > + if (ACPI_FAILURE(status)) > + return -EIO; > + > + return 0; > +} > + > +static int ideapad_wmi_audiomute_led_set(struct led_classdev *led_cdev, > + enum led_brightness brightness) > + > +{ > + return ideapad_wmi_mute_led_set(AUDIO_MUTE, led_cdev, brightness); } > + > +static int ideapad_wmi_micmute_led_set(struct led_classdev *led_cdev, > + enum led_brightness brightness) { > + return ideapad_wmi_mute_led_set(MIC_MUTE, led_cdev, brightness); } > + > +static int ideapad_wmi_leds_init(enum mute_led_type led_type, struct > +device *dev) { > + struct ideapad_wmi_private *wpriv = dev_get_drvdata(dev); > + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; > + struct acpi_buffer input; > + union acpi_object *obj; > + int led_version, err = 0; > + unsigned int wmiarg; > + acpi_status status; > + > + if (led_type == MIC_MUTE) > + wmiarg = WMI_LUD_GET_MICMUTE_LED_VER; > + else if (led_type == AUDIO_MUTE) > + wmiarg = WMI_LUD_GET_AUDIOMUTE_LED_VER; > + else > + return -EINVAL; > + > + input.length = sizeof(wmiarg); > + input.pointer = &wmiarg; > + status = wmidev_evaluate_method(led_wdev, 0, WMI_LUD_GET_SUPPORT, &input, &output); > + if (ACPI_FAILURE(status)) { > + kfree(output.pointer); > + return -EIO; > + } > + obj = output.pointer; > + led_version = obj->integer.value; > + > + wpriv->cdev[led_type].max_brightness = LED_ON; > + wpriv->cdev[led_type].dev = dev; > + wpriv->cdev[led_type].flags = LED_CORE_SUSPENDRESUME; > + > + if (led_type == MIC_MUTE) { > + if (led_version != WMI_LUD_SUPPORT_MICMUTE_LED_VER) { > + dev_info(dev, "This product doesn't support mic mute LED.\n"); > + return -EIO; > + } > + wpriv->cdev[led_type].name = "platform::micmute"; > + wpriv->cdev[led_type].brightness_set_blocking = &ideapad_wmi_micmute_led_set; > + wpriv->cdev[led_type].default_trigger = "audio-micmute"; > + > + err = devm_led_classdev_register(dev, &wpriv->cdev[led_type]); > + if (err < 0) { > + dev_err(dev, "Could not register mic mute LED : %d\n", err); > + led_classdev_unregister(&wpriv->cdev[led_type]); > + } > + } else { > + if (led_version != WMI_LUD_SUPPORT_AUDIOMUTE_LED_VER) { > + dev_info(dev, "This product doesn't support audio mute LED.\n"); > + return -EIO; > + } > + wpriv->cdev[led_type].name = "platform::mute"; > + wpriv->cdev[led_type].brightness_set_blocking = &ideapad_wmi_audiomute_led_set; > + wpriv->cdev[led_type].default_trigger = "audio-mute"; > + > + err = devm_led_classdev_register(dev, &wpriv->cdev[led_type]); > + if (err < 0) { > + dev_err(dev, "Could not register audio mute LED: %d\n", err); > + led_classdev_unregister(&wpriv->cdev[led_type]); > + } > + } > + > + kfree(obj); > + return err; > +} > + > +static void ideapad_wmi_leds_setup(struct device *dev) { > + ideapad_wmi_leds_init(MIC_MUTE, dev); > + ideapad_wmi_leds_init(AUDIO_MUTE, dev); } > + > static int ideapad_wmi_probe(struct wmi_device *wdev, const void *context) > { > struct ideapad_wmi_private *wpriv; > @@ -2043,6 +2183,12 @@ static int ideapad_wmi_probe(struct wmi_device *wdev, const void *context) > *wpriv = *(const struct ideapad_wmi_private *)context; > > dev_set_drvdata(&wdev->dev, wpriv); > + > + if (wpriv->event == IDEAPAD_WMI_EVENT_LUD_KEYS) { > + led_wdev = wdev; > + ideapad_wmi_leds_setup(&wdev->dev); > + } > + > return 0; > } > > @@ -2088,6 +2234,9 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data) > data->integer.value | IDEAPAD_WMI_KEY); > > break; > + case IDEAPAD_WMI_EVENT_LUD_KEYS: > + break; > + > } > } > > @@ -2099,10 +2248,16 @@ static const struct ideapad_wmi_private ideapad_wmi_context_fn_keys = { > .event = IDEAPAD_WMI_EVENT_FN_KEYS > }; > > +static const struct ideapad_wmi_private ideapad_wmi_context_LUD_keys = { > + .event = IDEAPAD_WMI_EVENT_LUD_KEYS > +}; > + > static const struct wmi_device_id ideapad_wmi_ids[] = { > { "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", &ideapad_wmi_context_esc }, /* Yoga 3 */ > { "56322276-8493-4CE8-A783-98C991274F5E", &ideapad_wmi_context_esc }, /* Yoga 700 */ > - { "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", &ideapad_wmi_context_fn_keys }, /* Legion 5 */ > + { "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", &ideapad_wmi_context_fn_keys }, /* FN keys */ > + { "CE6C0974-0407-4F50-88BA-4FC3B6559AD8", > +&ideapad_wmi_context_LUD_keys }, /* Util data */ > + > {}, > }; > MODULE_DEVICE_TABLE(wmi, ideapad_wmi_ids);