From: Krishna Chomal <krishna.chomal108@gmail.com>
To: foobisdweik <dweikmferris@gmail.com>
Cc: "Hans de Goede" <hansg@kernel.org>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
platform-driver-x86@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/2] platform/x86: hp-wmi: Expose independent CPU/GPU pwm channels
Date: Wed, 20 May 2026 15:51:14 +0530 [thread overview]
Message-ID: <ag2DCOGyCt7MBUL6@archlinux> (raw)
In-Reply-To: <20260513193916.84673-3-dweikmferris@gmail.com>
On Wed, May 13, 2026 at 12:39:16PM -0700, foobisdweik wrote:
>The Victus-S WMI fan-speed-set query (HPWMI_VICTUS_S_FAN_SPEED_SET_QUERY,
>GM2E in firmware) takes a two-byte buffer [cpu, gpu] and writes the
>values to EC SRP1/SRP2 setpoints independently. The driver however drove
>both fans from a single hwmon pwm1 attribute, with the GPU value
>derived as cpu + priv->gpu_delta from the fan table. This makes
>asymmetric fan curves (common ask: GPU fan louder than CPU under
>heavy gaming load) impossible from userspace fan-control tools that
>expect one pwm attribute per fan.
>
>Promote priv->pwm to a two-element array, add a second pwm hwmon
>channel, and extend hp_wmi_hwmon_write() / hp_wmi_apply_fan_settings()
>to drive the per-channel setpoints through GM2E.
>
>The U8_MAX sentinel in hp_wmi_fan_speed_set() selects the new
>per-fan path; existing callers using HP_FAN_SPEED_AUTOMATIC or a
>direct rpm value keep their behavior. The second pwm channel is only
>visible on Victus-S-capable boards, since other HP laptops still use
>the legacy single-fan WMI commands.
>
>Tested on OMEN by HP Laptop 16-b1xxx (8A13): writing pwm1=60, pwm2=200
>in manual mode drives CPU fan to ~1600 RPM and GPU fan to ~5700 RPM
>simultaneously, verified via fan1_input/fan2_input tachs and direct
>read of EC offsets 0xB0..0xB3.
>
>Signed-off-by: foobisdweik <dweikmferris@gmail.com>
>---
> drivers/platform/x86/hp/hp-wmi.c | 45 +++++++++++++++++++++++---------
> 1 file changed, 32 insertions(+), 13 deletions(-)
>
>diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
>index 389506a6d2e3..40eb3715583a 100644
>--- a/drivers/platform/x86/hp/hp-wmi.c
>+++ b/drivers/platform/x86/hp/hp-wmi.c
>@@ -486,7 +486,7 @@ struct hp_wmi_hwmon_priv {
> u8 max_rpm;
> int gpu_delta;
> u8 mode;
>- u8 pwm;
>+ u8 pwm[2];
> struct delayed_work keep_alive_dwork;
> };
>
>@@ -822,12 +822,18 @@ static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv, u8 speed)
> fan_speed[GPU_FAN] = speed;
>
> /*
>- * GPU fan speed is always a little higher than CPU fan speed, we fetch
>- * this delta value from the fan table during hwmon init.
>- * Exception: Speed is set to HP_FAN_SPEED_AUTOMATIC, to revert to
>- * automatic mode.
>+ * Pass-through value U8_MAX: drive each fan from its own
>+ * priv->pwm[] setpoint converted via pwm_to_rpm(). Used by the
>+ * hwmon pwm1/pwm2 path that allows independent CPU/GPU fan control.
>+ *
>+ * Otherwise: GPU fan speed is always a little higher than CPU fan
>+ * speed; we fetch this delta from the fan table during hwmon init.
>+ * Exception: HP_FAN_SPEED_AUTOMATIC reverts to automatic mode.
> */
>- if (speed != HP_FAN_SPEED_AUTOMATIC) {
>+ if (speed == U8_MAX) {
>+ fan_speed[CPU_FAN] = pwm_to_rpm(priv->pwm[CPU_FAN], priv);
>+ fan_speed[GPU_FAN] = pwm_to_rpm(priv->pwm[GPU_FAN], priv);
>+ } else if (speed != HP_FAN_SPEED_AUTOMATIC) {
> gpu_speed = speed + priv->gpu_delta;
> fan_speed[GPU_FAN] = clamp_val(gpu_speed, 0, U8_MAX);
> }
Currently, there are 2 callers for hp_wmi_fan_speed_set:
1. hp_wmi_fan_speed_reset() -> calls via speed = HP_FAN_SPEED_AUTOMATIC,
and
2. hp_wmi_apply_fan_settings() -> calls via speed = U8_MAX (as updated
by you)
So this else-if block is effectively never executed, which in turn means
that gpu_delta is also now redundant.
>@@ -2398,7 +2404,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
> case PWM_MODE_MANUAL:
> if (!is_victus_s_thermal_profile())
> return -EOPNOTSUPP;
>- ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv));
>+ ret = hp_wmi_fan_speed_set(priv, U8_MAX);
I think you could avoid passing the sentinel argument (speed) and
directly update priv->pwm[] in the caller: hp_wmi_fan_speed_reset()
> if (ret < 0)
> return ret;
> mod_delayed_work(system_wq, &priv->keep_alive_dwork,
>@@ -2429,6 +2435,12 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data,
> {
> switch (type) {
> case hwmon_pwm:
>+ /*
>+ * Second pwm channel only exists on Victus-S-style boards
>+ * which expose an independent GPU fan setpoint.
>+ */
>+ if (channel == 1 && !is_victus_s_thermal_profile())
>+ return 0;
Technically, the fan table has a dedicated field .num_fans (see
struct victus_s_fan_table_header), so there is a possibility that some
Victus-S-style device may not have second fan for pwm channel. But at
least for the current entries, this is not the case, so I guess it would
be safe to keep it this way.
> if (attr == hwmon_pwm_input && !is_victus_s_thermal_profile())
> return 0;
> return 0644;
>@@ -2514,7 +2526,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> /* ensure PWM input is within valid fan speeds */
> rpm = pwm_to_rpm(val, priv);
> rpm = clamp_val(rpm, priv->min_rpm, priv->max_rpm);
>- priv->pwm = rpm_to_pwm(rpm, priv);
>+ priv->pwm[channel] = rpm_to_pwm(rpm, priv);
> return hp_wmi_apply_fan_settings(priv);
> }
> switch (val) {
>@@ -2525,13 +2537,18 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> if (!is_victus_s_thermal_profile())
> return -EOPNOTSUPP;
> /*
>- * When switching to manual mode, set fan speed to
>- * current RPM values to ensure a smooth transition.
>+ * When switching to manual mode, seed each per-fan
>+ * setpoint from its current measured RPM so the
>+ * transition is smooth.
> */
>- rpm = hp_wmi_get_fan_speed_victus_s(channel);
>+ rpm = hp_wmi_get_fan_speed_victus_s(CPU_FAN);
>+ if (rpm < 0)
>+ return rpm;
>+ priv->pwm[CPU_FAN] = rpm_to_pwm(rpm / 100, priv);
>+ rpm = hp_wmi_get_fan_speed_victus_s(GPU_FAN);
> if (rpm < 0)
> return rpm;
>- priv->pwm = rpm_to_pwm(rpm / 100, priv);
>+ priv->pwm[GPU_FAN] = rpm_to_pwm(rpm / 100, priv);
> priv->mode = PWM_MODE_MANUAL;
> return hp_wmi_apply_fan_settings(priv);
> case PWM_MODE_AUTO:
>@@ -2547,7 +2564,9 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
>
> static const struct hwmon_channel_info * const info[] = {
> HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT),
>- HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE | HWMON_PWM_INPUT),
>+ HWMON_CHANNEL_INFO(pwm,
>+ HWMON_PWM_ENABLE | HWMON_PWM_INPUT,
>+ HWMON_PWM_ENABLE | HWMON_PWM_INPUT),
I think you can take this opportunity to explore adding
fanX_{min,max,input} to hwmon as well.
> NULL
> };
>
>--
>2.54.0
>
prev parent reply other threads:[~2026-05-20 10:21 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-13 19:39 [PATCH 0/2] hp-wmi: Omen 16-b1xxx (8A13) support + per-fan pwm foobisdweik
2026-05-13 19:39 ` [PATCH 1/2] platform/x86: hp-wmi: Add support for Omen 16-b1xxx (8A13) foobisdweik
2026-05-19 13:58 ` Ilpo Järvinen
2026-05-20 9:46 ` Krishna Chomal
2026-05-13 19:39 ` [PATCH 2/2] platform/x86: hp-wmi: Expose independent CPU/GPU pwm channels foobisdweik
2026-05-20 10:21 ` Krishna Chomal [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=ag2DCOGyCt7MBUL6@archlinux \
--to=krishna.chomal108@gmail.com \
--cc=dweikmferris@gmail.com \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--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 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.