From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: "Derek J. Clark" <derekjohn.clark@gmail.com>
Cc: Hans de Goede <hdegoede@redhat.com>,
Jonathan Corbet <corbet@lwn.net>,
Mario Limonciello <superm1@kernel.org>,
Luke Jones <luke@ljones.dev>, Xino Ni <nijs1@lenovo.com>,
Zhixin Zhang <zhangzx36@lenovo.com>,
Mia Shao <shaohz1@lenovo.com>,
Mark Pearson <mpearson-lenovo@squebb.ca>,
"Pierre-Loup A . Griffais" <pgriffais@valvesoftware.com>,
"Cody T . -H . Chiu" <codyit@gmail.com>,
John Martens <johnfanv2@gmail.com>,
platform-driver-x86@vger.kernel.org, linux-doc@vger.kernel.org,
LKML <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2 2/4] platform/x86: Add Lenovo GameZone WMI Driver
Date: Fri, 10 Jan 2025 14:27:21 +0200 (EET) [thread overview]
Message-ID: <487ff49a-b521-600d-16bf-db3122295812@linux.intel.com> (raw)
In-Reply-To: <20250102004854.14874-3-derekjohn.clark@gmail.com>
On Wed, 1 Jan 2025, Derek J. Clark wrote:
> Adds lenovo-wmi-gamezone.c which provides a driver for the Lenovo
> GameZone WMI interface that comes on Lenovo "Gaming Series" hardware.
> Provides ACPI platform profiles over WMI.
>
> v2:
> - Use devm_kzalloc to ensure driver can be instanced, remove global
> reference.
> - Ensure reverse Christmas tree for all variable declarations.
> - Remove extra whitespace.
> - Use guard(mutex) in all mutex instances, global mutex.
> - Use pr_fmt instead of adding the driver name to each pr_err.
> - Remove noisy pr_info usage.
> - Rename gamezone_wmi to lenovo_wmi_gz_priv and gz_wmi to priv.
> - Remove GZ_WMI symbol exporting.
>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
> MAINTAINERS | 7 +
> drivers/platform/x86/Kconfig | 11 ++
> drivers/platform/x86/Makefile | 1 +
> drivers/platform/x86/lenovo-wmi-gamezone.c | 203 +++++++++++++++++++++
> drivers/platform/x86/lenovo-wmi.h | 105 +++++++++++
> 5 files changed, 327 insertions(+)
> create mode 100644 drivers/platform/x86/lenovo-wmi-gamezone.c
> create mode 100644 drivers/platform/x86/lenovo-wmi.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index baf0eeb9a355..8f8a6aec6b92 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -13034,6 +13034,13 @@ S: Maintained
> W: http://legousb.sourceforge.net/
> F: drivers/usb/misc/legousbtower.c
>
> +LENOVO WMI drivers
> +M: Derek J. Clark <derekjohn.clark@gmail.com>
> +L: platform-driver-x86@vger.kernel.org
> +S: Maintained
> +F: drivers/platform/x86/lenovo-wmi-gamezone.c
> +F: drivers/platform/x86/lenovo-wmi.h
> +
> LETSKETCH HID TABLET DRIVER
> M: Hans de Goede <hdegoede@redhat.com>
> L: linux-input@vger.kernel.org
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 0258dd879d64..9a6ac7fdec9f 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -459,6 +459,17 @@ config IBM_RTL
> state = 0 (BIOS SMIs on)
> state = 1 (BIOS SMIs off)
>
> +config LENOVO_WMI_GAMEZONE
> + tristate "Lenovo GameZone WMI Driver"
> + depends on ACPI_WMI
> + select ACPI_PLATFORM_PROFILE
> + help
> + Say Y here if you have a WMI aware Lenovo Legion device and would like to use the
> + platform-profile firmware interface.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called lenovo_wmi_gamezone.
> +
> config IDEAPAD_LAPTOP
> tristate "Lenovo IdeaPad Laptop Extras"
> depends on ACPI
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index e1b142947067..7cb29a480ed2 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
> obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o
> obj-$(CONFIG_YT2_1380) += lenovo-yoga-tab2-pro-1380-fastcharger.o
> obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o
> +obj-$(CONFIG_LENOVO_WMI_GAMEZONE) += lenovo-wmi-gamezone.o
>
> # Intel
> obj-y += intel/
> diff --git a/drivers/platform/x86/lenovo-wmi-gamezone.c b/drivers/platform/x86/lenovo-wmi-gamezone.c
> new file mode 100644
> index 000000000000..da5e2bc41f39
> --- /dev/null
> +++ b/drivers/platform/x86/lenovo-wmi-gamezone.c
> @@ -0,0 +1,203 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Lenovo GameZone WMI interface driver. The GameZone WMI interface provides
> + * platform profile and fan curve settings for devices that fall under the
> + * "Gaming Series" of Lenovo Legion devices.
> + *
> + * Copyright(C) 2024 Derek J. Clark <derekjohn.clark@gmail.com>
> + */
> +
> +#include <linux/platform_profile.h>
> +#include "lenovo-wmi.h"
> +
> +#define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
> +
> +/* Method IDs */
> +#define WMI_METHOD_ID_SMARTFAN_SUPP 43 /* IsSupportSmartFan */
> +#define WMI_METHOD_ID_SMARTFAN_SET 44 /* SetSmartFanMode */
> +#define WMI_METHOD_ID_SMARTFAN_GET 45 /* GetSmartFanMode */
> +
> +static DEFINE_MUTEX(call_mutex);
> +
> +static const struct wmi_device_id lenovo_wmi_gamezone_id_table[] = {
> + { LENOVO_GAMEZONE_GUID, NULL }, /* LENOVO_GAMEZONE_DATA */
> + {}
> +};
> +
> +struct lenovo_wmi_gz_priv {
> + struct wmi_device *wdev;
> + enum platform_profile_option current_profile;
> + struct platform_profile_handler pprof;
> + bool platform_profile_support;
> +};
> +
> +/* Platform Profile Methods */
> +static int lenovo_wmi_gamezone_platform_profile_supported(
> + struct platform_profile_handler *pprof, int *supported)
> +{
> + struct lenovo_wmi_gz_priv *priv;
> +
> + priv = container_of(pprof, struct lenovo_wmi_gz_priv, pprof);
> +
> + guard(mutex)(&call_mutex);
> + return lenovo_wmidev_evaluate_method_1(
> + priv->wdev, 0x0, WMI_METHOD_ID_SMARTFAN_SUPP, 0, supported);
> +}
> +
> +static int
> +lenovo_wmi_gamezone_profile_get(struct platform_profile_handler *pprof,
> + enum platform_profile_option *profile)
> +{
> + struct lenovo_wmi_gz_priv *priv;
> + int sel_prof;
> + int err;
> +
> + priv = container_of(pprof, struct lenovo_wmi_gz_priv, pprof);
> +
> + guard(mutex)(&call_mutex);
> + err = lenovo_wmidev_evaluate_method_1(
> + priv->wdev, 0x0, WMI_METHOD_ID_SMARTFAN_GET, 0, &sel_prof);
> + if (err) {
> + pr_err("Error getting fan profile from WMI interface: %d\n",
> + err);
> + return err;
> + }
> +
> + switch (sel_prof) {
> + case SMARTFAN_MODE_QUIET:
> + *profile = PLATFORM_PROFILE_QUIET;
> + break;
> + case SMARTFAN_MODE_BALANCED:
> + *profile = PLATFORM_PROFILE_BALANCED;
> + break;
> + case SMARTFAN_MODE_PERFORMANCE:
> + *profile = PLATFORM_PROFILE_PERFORMANCE;
> + break;
> + case SMARTFAN_MODE_CUSTOM:
> + *profile = PLATFORM_PROFILE_CUSTOM;
> + break;
> + default:
> + return -EINVAL;
> + }
> + priv->current_profile = *profile;
> +
> + return 0;
> +}
> +
> +static int
> +lenovo_wmi_gamezone_profile_set(struct platform_profile_handler *pprof,
> + enum platform_profile_option profile)
> +{
> + struct lenovo_wmi_gz_priv *priv;
> + int sel_prof;
> + int err;
> +
> + switch (profile) {
> + case PLATFORM_PROFILE_QUIET:
> + sel_prof = SMARTFAN_MODE_QUIET;
> + break;
> + case PLATFORM_PROFILE_BALANCED:
> + sel_prof = SMARTFAN_MODE_BALANCED;
> + break;
> + case PLATFORM_PROFILE_PERFORMANCE:
> + sel_prof = SMARTFAN_MODE_PERFORMANCE;
> + break;
> + case PLATFORM_PROFILE_CUSTOM:
> + sel_prof = SMARTFAN_MODE_CUSTOM;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + priv = container_of(pprof, struct lenovo_wmi_gz_priv, pprof);
> +
> + guard(mutex)(&call_mutex);
> + err = lenovo_wmidev_evaluate_method_1(
> + priv->wdev, 0x0, WMI_METHOD_ID_SMARTFAN_SET, sel_prof, NULL);
> + if (err) {
> + pr_err("Error setting fan profile on WMI interface: %u\n", err);
> + return err;
> + }
> +
> + priv->current_profile = profile;
> + return 0;
> +}
> +
> +/* Driver Setup */
> +static int platform_profile_setup(struct lenovo_wmi_gz_priv *priv)
> +{
> + int supported;
> + int err;
> +
> + err = lenovo_wmi_gamezone_platform_profile_supported(&priv->pprof,
> + &supported);
> + if (err) {
> + pr_err("Error checking platform profile support: %d\n", err);
> + return err;
> + }
> +
> + priv->platform_profile_support = supported;
> +
> + if (!supported)
> + return -EOPNOTSUPP;
> +
> + priv->pprof.name = "lenovo-wmi-gamezone";
> + priv->pprof.profile_get = lenovo_wmi_gamezone_profile_get;
> + priv->pprof.profile_set = lenovo_wmi_gamezone_profile_set;
> +
> + set_bit(PLATFORM_PROFILE_QUIET, priv->pprof.choices);
> + set_bit(PLATFORM_PROFILE_BALANCED, priv->pprof.choices);
> + set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->pprof.choices);
> + set_bit(PLATFORM_PROFILE_CUSTOM, priv->pprof.choices);
> +
> + err = lenovo_wmi_gamezone_profile_get(&priv->pprof,
> + &priv->current_profile);
> + if (err) {
> + pr_err("Error getting current platform profile: %d\n", err);
> + return err;
> + }
> +
> + guard(mutex)(&call_mutex);
> + err = platform_profile_register(&priv->pprof);
> + if (err) {
> + pr_err("Error registering platform profile: %d\n", err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int lenovo_wmi_gamezone_probe(struct wmi_device *wdev,
> + const void *context)
> +{
> + struct lenovo_wmi_gz_priv *priv;
> +
> + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->wdev = wdev;
> + return platform_profile_setup(priv);
> +}
> +
> +static void lenovo_wmi_gamezone_remove(struct wmi_device *wdev)
> +{
> + struct lenovo_wmi_gz_priv *priv = dev_get_drvdata(&wdev->dev);
> +
> + guard(mutex)(&call_mutex);
> + platform_profile_remove(&priv->pprof);
> +}
> +
> +static struct wmi_driver lenovo_wmi_gamezone_driver = {
> + .driver = { .name = "lenovo_wmi_gamezone" },
> + .id_table = lenovo_wmi_gamezone_id_table,
> + .probe = lenovo_wmi_gamezone_probe,
> + .remove = lenovo_wmi_gamezone_remove,
> +};
> +
> +module_wmi_driver(lenovo_wmi_gamezone_driver);
> +
> +MODULE_DEVICE_TABLE(wmi, lenovo_wmi_gamezone_id_table);
> +MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
> +MODULE_DESCRIPTION("Lenovo GameZone WMI Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/platform/x86/lenovo-wmi.h b/drivers/platform/x86/lenovo-wmi.h
> new file mode 100644
> index 000000000000..8a302c6c47cb
> --- /dev/null
> +++ b/drivers/platform/x86/lenovo-wmi.h
> @@ -0,0 +1,105 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Lenovo Legion WMI interface driver. The Lenovo Legion WMI interface is
> + * broken up into multiple GUID interfaces that require cross-references
> + * between GUID's for some functionality. The "Custom Mode" interface is a
> + * legacy interface for managing and displaying CPU & GPU power and hwmon
> + * settings and readings. The "Other Mode" interface is a modern interface
> + * that replaces or extends the "Custom Mode" interface methods. The
> + * "GameZone" interface adds advanced features such as fan profiles and
> + * overclocking. The "Lighting" interface adds control of various status
> + * lights related to different hardware components. "Other Method" uses
> + * the data structs LENOVO_CAPABILITY_DATA_00, LENOVO_CAPABILITY_DATA_01
> + * and LENOVO_CAPABILITY_DATA_02 structs for capability information.
> + *
> + * Copyright(C) 2024 Derek J. Clark <derekjohn.clark@gmail.com>
> + *
> + */
> +
> +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
Don't include __func__ into pr_fmt(). Including __func__ into any >dbg
level message is not helpful to user, the error/warning/info should be
written in plain English, not in terms of code/function names.
The usual pr_fmt() boilerplate is this:
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
--
i.
> +
> +#ifndef _LENOVO_WMI_H_
> +#define _LENOVO_WMI_H_
> +
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +#include <linux/wmi.h>
> +
> +/* Platform Profile Modes */
> +#define SMARTFAN_MODE_QUIET 0x01
> +#define SMARTFAN_MODE_BALANCED 0x02
> +#define SMARTFAN_MODE_PERFORMANCE 0x03
> +#define SMARTFAN_MODE_CUSTOM 0xFF
> +
> +struct wmi_method_args {
> + u32 arg0;
> + u32 arg1;
> +};
> +
> +/* General Use functions */
> +static int lenovo_wmidev_evaluate_method(struct wmi_device *wdev, u8 instance,
> + u32 method_id, struct acpi_buffer *in,
> + struct acpi_buffer *out)
> +{
> + acpi_status status;
> +
> + status = wmidev_evaluate_method(wdev, instance, method_id, in, out);
> +
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + return 0;
> +};
> +
> +int lenovo_wmidev_evaluate_method_2(struct wmi_device *wdev, u8 instance,
> + u32 method_id, u32 arg0, u32 arg1,
> + u32 *retval);
> +
> +int lenovo_wmidev_evaluate_method_2(struct wmi_device *wdev, u8 instance,
> + u32 method_id, u32 arg0, u32 arg1,
> + u32 *retval)
> +{
> + struct wmi_method_args args = { arg0, arg1 };
> + struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
> + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> + union acpi_object *ret_obj = NULL;
> + int err;
> +
> + err = lenovo_wmidev_evaluate_method(wdev, instance, method_id, &input,
> + &output);
> +
> + if (err) {
> + pr_err("Attempt to get method value failed.\n");
> + return err;
> + }
> +
> + if (retval) {
> + ret_obj = (union acpi_object *)output.pointer;
> + if (!ret_obj) {
> + pr_err("Failed to get valid ACPI object from WMI interface\n");
> + return -EIO;
> + }
> + if (ret_obj->type != ACPI_TYPE_INTEGER) {
> + pr_err("WMI query returnd ACPI object with wrong type.\n");
> + kfree(ret_obj);
> + return -EIO;
> + }
> + *retval = (u32)ret_obj->integer.value;
> + }
> +
> + kfree(ret_obj);
> +
> + return 0;
> +}
> +
> +int lenovo_wmidev_evaluate_method_1(struct wmi_device *wdev, u8 instance,
> + u32 method_id, u32 arg0, u32 *retval);
> +
> +int lenovo_wmidev_evaluate_method_1(struct wmi_device *wdev, u8 instance,
> + u32 method_id, u32 arg0, u32 *retval)
> +{
> + return lenovo_wmidev_evaluate_method_2(wdev, instance, method_id, arg0,
> + 0, retval);
> +}
> +
> +#endif /* !_LENOVO_WMI_H_ */
>
next prev parent reply other threads:[~2025-01-10 12:27 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-01-02 0:47 [PATCH v2 0/4] platform/x86: Add Lenovo Gaming Series WMI Drivers Derek J. Clark
2025-01-02 0:47 ` [PATCH v2 1/4] platform/x86: Add lenovo-wmi drivers Documentation Derek J. Clark
2025-01-02 3:46 ` Mario Limonciello
2025-01-09 21:36 ` Armin Wolf
2025-01-10 22:41 ` Derek John Clark
2025-01-10 23:21 ` Armin Wolf
2025-01-02 0:47 ` [PATCH v2 2/4] platform/x86: Add Lenovo GameZone WMI Driver Derek J. Clark
2025-01-02 4:09 ` Mario Limonciello
2025-01-02 18:44 ` Derek John Clark
2025-01-02 19:10 ` Mario Limonciello
2025-01-09 22:11 ` Armin Wolf
2025-01-10 21:33 ` Derek John Clark
2025-01-10 23:23 ` Armin Wolf
2025-01-12 3:25 ` Derek John Clark
2025-01-10 12:27 ` Ilpo Järvinen [this message]
2025-01-10 21:34 ` Derek John Clark
2025-01-02 0:47 ` [PATCH v2 3/4] platform/x86: Add Lenovo Capability Data 01 " Derek J. Clark
2025-01-02 3:44 ` Mario Limonciello
2025-01-02 18:42 ` Derek John Clark
2025-01-09 22:34 ` Armin Wolf
2025-01-10 22:11 ` Derek John Clark
2025-01-11 0:01 ` Armin Wolf
2025-01-02 0:47 ` [PATCH v2 4/4] platform/x86: Add Lenovo Other Mode " Derek J. Clark
2025-01-02 3:40 ` Mario Limonciello
2025-01-02 18:49 ` Derek John Clark
2025-01-07 18:21 ` Ilpo Järvinen
2025-01-07 23:55 ` Derek John Clark
2025-01-08 9:37 ` Ilpo Järvinen
2025-01-02 9:33 ` kernel test robot
2025-01-09 23:00 ` Armin Wolf
2025-01-10 22:33 ` Derek John Clark
2025-01-11 0:10 ` Armin Wolf
2025-01-11 17:29 ` Derek John Clark
2025-01-02 4:01 ` [PATCH v2 0/4] platform/x86: Add Lenovo Gaming Series WMI Drivers Mario Limonciello
2025-01-02 18:27 ` Derek John Clark
2025-01-09 23:20 ` Armin Wolf
2025-01-10 21:52 ` Derek John Clark
2025-01-11 0:25 ` Armin Wolf
2025-01-11 17:13 ` Derek John Clark
2025-01-08 23:09 ` 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=487ff49a-b521-600d-16bf-db3122295812@linux.intel.com \
--to=ilpo.jarvinen@linux.intel.com \
--cc=codyit@gmail.com \
--cc=corbet@lwn.net \
--cc=derekjohn.clark@gmail.com \
--cc=hdegoede@redhat.com \
--cc=johnfanv2@gmail.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luke@ljones.dev \
--cc=mpearson-lenovo@squebb.ca \
--cc=nijs1@lenovo.com \
--cc=pgriffais@valvesoftware.com \
--cc=platform-driver-x86@vger.kernel.org \
--cc=shaohz1@lenovo.com \
--cc=superm1@kernel.org \
--cc=zhangzx36@lenovo.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.