All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control
@ 2026-05-07 10:13 jR4dh3y
  2026-05-07 11:20 ` Ilpo Järvinen
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: jR4dh3y @ 2026-05-07 10:13 UTC (permalink / raw)
  To: platform-driver-x86; +Cc: ilpo.jarvinen, hdegoede, jR4dh3y

HP Victus 15-fb0xxx board 8A3D supports the same WMI fan speed
set command used by the Victus manual fan control path, but it is
not a Victus S thermal-profile board.

Enable the hwmon PWM manual fan interface for this board without
adding it to the Victus S thermal profile DMI table. This preserves
the existing generic platform-profile choices on this machine.

The board does not expose a Victus S fan table, so use the RPM limits
validated on hardware: CPU fan 2600-5800 RPM, GPU fan about 300 RPM
higher.

Compile-tested against 7.0.3-1-cachyos-bore-lto. The WMI fan-speed
set path was validated on a Victus by HP Gaming Laptop 15-fb0xxx with
board 8A3D and BIOS F.22.

Signed-off-by: jR4dh3y <radheykalra901@gmail.com>
---
 drivers/platform/x86/hp/hp-wmi.c | 56 +++++++++++++++++++++++++-------
 1 file changed, 45 insertions(+), 11 deletions(-)

diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index 304d9ac..b2a6d50 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -204,6 +204,11 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
 	{},
 };
 
+/* DMI Board names of Victus laptops with the Victus fan-control WMI calls. */
+static const char * const victus_fan_control_boards[] = {
+	"8A3D", /* Victus by HP Gaming Laptop 15-fb0xxx */
+};
+
 static bool is_victus_s_board;
 
 enum hp_wmi_radio {
@@ -1715,6 +1720,22 @@ static bool is_victus_s_thermal_profile(void)
 	return is_victus_s_board;
 }
 
+static bool is_victus_fan_control(void)
+{
+	const char *board_name;
+
+	if (is_victus_s_thermal_profile())
+		return true;
+
+	board_name = dmi_get_system_info(DMI_BOARD_NAME);
+	if (!board_name)
+		return false;
+
+	return match_string(victus_fan_control_boards,
+			    ARRAY_SIZE(victus_fan_control_boards),
+			    board_name) >= 0;
+}
+
 static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,
 					    bool *ppab_enable,
 					    u8 *dstate,
@@ -2324,7 +2345,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
 
 	switch (priv->mode) {
 	case PWM_MODE_MAX:
-		if (is_victus_s_thermal_profile())
+		if (is_victus_fan_control())
 			hp_wmi_get_fan_count_userdefine_trigger();
 		ret = hp_wmi_fan_speed_max_set(1);
 		if (ret < 0)
@@ -2333,7 +2354,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
 				      secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
 		return 0;
 	case PWM_MODE_MANUAL:
-		if (!is_victus_s_thermal_profile())
+		if (!is_victus_fan_control())
 			return -EOPNOTSUPP;
 		ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv));
 		if (ret < 0)
@@ -2342,7 +2363,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
 				      secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
 		return 0;
 	case PWM_MODE_AUTO:
-		if (is_victus_s_thermal_profile()) {
+		if (is_victus_fan_control()) {
 			hp_wmi_get_fan_count_userdefine_trigger();
 			ret = hp_wmi_fan_speed_max_reset(priv);
 		} else {
@@ -2366,7 +2387,7 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data,
 {
 	switch (type) {
 	case hwmon_pwm:
-		if (attr == hwmon_pwm_input && !is_victus_s_thermal_profile())
+		if (attr == hwmon_pwm_input && !is_victus_fan_control())
 			return 0;
 		return 0644;
 	case hwmon_fan:
@@ -2404,10 +2425,13 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 		return 0;
 	case hwmon_pwm:
 		if (attr == hwmon_pwm_input) {
-			if (!is_victus_s_thermal_profile())
+			if (!is_victus_fan_control())
 				return -EOPNOTSUPP;
 
-			rpm = hp_wmi_get_fan_speed_victus_s(channel);
+			if (is_victus_s_thermal_profile())
+				rpm = hp_wmi_get_fan_speed_victus_s(channel);
+			else
+				rpm = hp_wmi_get_fan_speed(channel);
 			if (rpm < 0)
 				return rpm;
 			*val = rpm_to_pwm(rpm / 100, priv);
@@ -2438,7 +2462,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
 	switch (type) {
 	case hwmon_pwm:
 		if (attr == hwmon_pwm_input) {
-			if (!is_victus_s_thermal_profile())
+			if (!is_victus_fan_control())
 				return -EOPNOTSUPP;
 			/* PWM input is invalid when not in manual mode */
 			if (priv->mode != PWM_MODE_MANUAL)
@@ -2455,13 +2479,16 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
 			priv->mode = PWM_MODE_MAX;
 			return hp_wmi_apply_fan_settings(priv);
 		case PWM_MODE_MANUAL:
-			if (!is_victus_s_thermal_profile())
+			if (!is_victus_fan_control())
 				return -EOPNOTSUPP;
 			/*
 			 * When switching to manual mode, set fan speed to
 			 * current RPM values to ensure a smooth transition.
 			 */
-			rpm = hp_wmi_get_fan_speed_victus_s(channel);
+			if (is_victus_s_thermal_profile())
+				rpm = hp_wmi_get_fan_speed_victus_s(channel);
+			else
+				rpm = hp_wmi_get_fan_speed(channel);
 			if (rpm < 0)
 				return rpm;
 			priv->pwm = rpm_to_pwm(rpm / 100, priv);
@@ -2519,9 +2546,16 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv)
 	/* Default behaviour on hwmon init is automatic mode */
 	priv->mode = PWM_MODE_AUTO;
 
-	/* Bypass all non-Victus S devices */
-	if (!is_victus_s_thermal_profile())
+	/* Bypass devices without the Victus fan-control WMI calls. */
+	if (!is_victus_fan_control())
+		return 0;
+
+	if (!is_victus_s_thermal_profile()) {
+		priv->min_rpm = 26;
+		priv->max_rpm = 58;
+		priv->gpu_delta = 3;
 		return 0;
+	}
 
 	ret = hp_wmi_perform_query(HPWMI_VICTUS_S_GET_FAN_TABLE_QUERY,
 				   HPWMI_GM, &fan_data, 4, sizeof(fan_data));
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control
  2026-05-07 10:13 [PATCH] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control jR4dh3y
@ 2026-05-07 11:20 ` Ilpo Järvinen
  2026-05-07 19:15 ` Krishna Chomal
  2026-05-16  9:05 ` [PATCH v2] " Radhey Kalra
  2 siblings, 0 replies; 5+ messages in thread
From: Ilpo Järvinen @ 2026-05-07 11:20 UTC (permalink / raw)
  To: jR4dh3y, Krishna Chomal; +Cc: platform-driver-x86, hdegoede

On Thu, 7 May 2026, jR4dh3y wrote:

> HP Victus 15-fb0xxx board 8A3D supports the same WMI fan speed
> set command used by the Victus manual fan control path, but it is
> not a Victus S thermal-profile board.
> 
> Enable the hwmon PWM manual fan interface for this board without
> adding it to the Victus S thermal profile DMI table. This preserves
> the existing generic platform-profile choices on this machine.
> 
> The board does not expose a Victus S fan table, so use the RPM limits
> validated on hardware: CPU fan 2600-5800 RPM, GPU fan about 300 RPM
> higher.
>
> Compile-tested against 7.0.3-1-cachyos-bore-lto. The WMI fan-speed
> set path was validated on a Victus by HP Gaming Laptop 15-fb0xxx with
> board 8A3D and BIOS F.22.
> 
> Signed-off-by: jR4dh3y <radheykalra901@gmail.com>

Hi,

The sign off should be based on your real name (sorry, no nicknames or 
like).

> ---
>  drivers/platform/x86/hp/hp-wmi.c | 56 +++++++++++++++++++++++++-------
>  1 file changed, 45 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
> index 304d9ac..b2a6d50 100644
> --- a/drivers/platform/x86/hp/hp-wmi.c
> +++ b/drivers/platform/x86/hp/hp-wmi.c
> @@ -204,6 +204,11 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
>  	{},
>  };
>  
> +/* DMI Board names of Victus laptops with the Victus fan-control WMI calls. */
> +static const char * const victus_fan_control_boards[] = {
> +	"8A3D", /* Victus by HP Gaming Laptop 15-fb0xxx */
> +};
> +
>  static bool is_victus_s_board;
>  
>  enum hp_wmi_radio {
> @@ -1715,6 +1720,22 @@ static bool is_victus_s_thermal_profile(void)
>  	return is_victus_s_board;
>  }
>  
> +static bool is_victus_fan_control(void)
> +{
> +	const char *board_name;
> +
> +	if (is_victus_s_thermal_profile())
> +		return true;
> +
> +	board_name = dmi_get_system_info(DMI_BOARD_NAME);
> +	if (!board_name)
> +		return false;
> +
> +	return match_string(victus_fan_control_boards,
> +			    ARRAY_SIZE(victus_fan_control_boards),
> +			    board_name) >= 0;
> +}

Instead of piling up more if()s and dmi checks everywhere, the approach 
current used for victus_s_thermal_profile_boards should be extended to 
cover a wider set of driver features and models including this one. The 
model specific differences should be (mostly) handled by what is stored 
into the struct in .driver_data.

If such an extension seems impossible for some reason, I might reconsider 
but it needs to be explored first.

> +
>  static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,
>  					    bool *ppab_enable,
>  					    u8 *dstate,
> @@ -2324,7 +2345,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
>  
>  	switch (priv->mode) {
>  	case PWM_MODE_MAX:
> -		if (is_victus_s_thermal_profile())
> +		if (is_victus_fan_control())
>  			hp_wmi_get_fan_count_userdefine_trigger();
>  		ret = hp_wmi_fan_speed_max_set(1);
>  		if (ret < 0)
> @@ -2333,7 +2354,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
>  				      secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
>  		return 0;
>  	case PWM_MODE_MANUAL:
> -		if (!is_victus_s_thermal_profile())
> +		if (!is_victus_fan_control())
>  			return -EOPNOTSUPP;
>  		ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv));
>  		if (ret < 0)
> @@ -2342,7 +2363,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
>  				      secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
>  		return 0;
>  	case PWM_MODE_AUTO:
> -		if (is_victus_s_thermal_profile()) {
> +		if (is_victus_fan_control()) {
>  			hp_wmi_get_fan_count_userdefine_trigger();
>  			ret = hp_wmi_fan_speed_max_reset(priv);
>  		} else {
> @@ -2366,7 +2387,7 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data,
>  {
>  	switch (type) {
>  	case hwmon_pwm:
> -		if (attr == hwmon_pwm_input && !is_victus_s_thermal_profile())
> +		if (attr == hwmon_pwm_input && !is_victus_fan_control())
>  			return 0;
>  		return 0644;
>  	case hwmon_fan:
> @@ -2404,10 +2425,13 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
>  		return 0;
>  	case hwmon_pwm:
>  		if (attr == hwmon_pwm_input) {
> -			if (!is_victus_s_thermal_profile())
> +			if (!is_victus_fan_control())
>  				return -EOPNOTSUPP;
>  
> -			rpm = hp_wmi_get_fan_speed_victus_s(channel);
> +			if (is_victus_s_thermal_profile())
> +				rpm = hp_wmi_get_fan_speed_victus_s(channel);
> +			else
> +				rpm = hp_wmi_get_fan_speed(channel);

Structure held in .driver_data should somehow tell which of these function 
to call or perhaps just store the function pointer (in which case NULL 
would imply -EOPNOTSUPP).

>  			if (rpm < 0)
>  				return rpm;
>  			*val = rpm_to_pwm(rpm / 100, priv);
> @@ -2438,7 +2462,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
>  	switch (type) {
>  	case hwmon_pwm:
>  		if (attr == hwmon_pwm_input) {
> -			if (!is_victus_s_thermal_profile())
> +			if (!is_victus_fan_control())
>  				return -EOPNOTSUPP;
>  			/* PWM input is invalid when not in manual mode */
>  			if (priv->mode != PWM_MODE_MANUAL)
> @@ -2455,13 +2479,16 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
>  			priv->mode = PWM_MODE_MAX;
>  			return hp_wmi_apply_fan_settings(priv);
>  		case PWM_MODE_MANUAL:
> -			if (!is_victus_s_thermal_profile())
> +			if (!is_victus_fan_control())
>  				return -EOPNOTSUPP;
>  			/*
>  			 * When switching to manual mode, set fan speed to
>  			 * current RPM values to ensure a smooth transition.
>  			 */
> -			rpm = hp_wmi_get_fan_speed_victus_s(channel);
> +			if (is_victus_s_thermal_profile())
> +				rpm = hp_wmi_get_fan_speed_victus_s(channel);
> +			else
> +				rpm = hp_wmi_get_fan_speed(channel);
>  			if (rpm < 0)
>  				return rpm;
>  			priv->pwm = rpm_to_pwm(rpm / 100, priv);
> @@ -2519,9 +2546,16 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv)
>  	/* Default behaviour on hwmon init is automatic mode */
>  	priv->mode = PWM_MODE_AUTO;
>  
> -	/* Bypass all non-Victus S devices */
> -	if (!is_victus_s_thermal_profile())
> +	/* Bypass devices without the Victus fan-control WMI calls. */
> +	if (!is_victus_fan_control())
> +		return 0;
> +
> +	if (!is_victus_s_thermal_profile()) {
> +		priv->min_rpm = 26;
> +		priv->max_rpm = 58;
> +		priv->gpu_delta = 3;

These should come from information stored into driver_data, not be 
hardcoded into code like this (if we really cannot get them from HW).

>  		return 0;
> +	}
>  
>  	ret = hp_wmi_perform_query(HPWMI_VICTUS_S_GET_FAN_TABLE_QUERY,
>  				   HPWMI_GM, &fan_data, 4, sizeof(fan_data));
> 

-- 
 i.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control
  2026-05-07 10:13 [PATCH] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control jR4dh3y
  2026-05-07 11:20 ` Ilpo Järvinen
@ 2026-05-07 19:15 ` Krishna Chomal
  2026-05-07 20:13   ` Radhey Kalra
  2026-05-16  9:05 ` [PATCH v2] " Radhey Kalra
  2 siblings, 1 reply; 5+ messages in thread
From: Krishna Chomal @ 2026-05-07 19:15 UTC (permalink / raw)
  To: jR4dh3y; +Cc: platform-driver-x86, ilpo.jarvinen, hdegoede

Hi,

On Thu, May 07, 2026 at 03:43:44PM +0530, jR4dh3y wrote:
>HP Victus 15-fb0xxx board 8A3D supports the same WMI fan speed
>set command used by the Victus manual fan control path, but it is
>not a Victus S thermal-profile board.
>

I wanted to clarify that the names in this driver are currently a bit
misleading since victus_s_thermal_profile_boards[] contains both Victus
and Omen devices. The main idea is that, devices which support fan
control should go in victus_s_thermal_profile_boards[].

>Enable the hwmon PWM manual fan interface for this board without
>adding it to the Victus S thermal profile DMI table. This preserves
>the existing generic platform-profile choices on this machine.
>
>The board does not expose a Victus S fan table, so use the RPM limits
>validated on hardware: CPU fan 2600-5800 RPM, GPU fan about 300 RPM
>higher.
>

This is really interesting because as per my understanding the OGH
software on Windows derives the fan curve for all fan-control supporting
devices via WMI calls. Can you explain why it "does not expose a Victus S
fan table"? Is it a WMI call failure or issue in parsing the table data?

NOTE: There is a known regression [1] causing a failure in parsing
the fan table so you should build against mainline 7.1-rc1 at least, or
manually cherry-pick 9d317a54e46d ("platform/x86: hp-wmi: fix fan table
parsing") into your tree.

[1] https://lore.kernel.org/all/CABd86uaQY9-ZSU-_PHKtZZyWnDFHLEsuaM+NBV5nOLWA+YSX7A@mail.gmail.com

>Compile-tested against 7.0.3-1-cachyos-bore-lto. The WMI fan-speed
>set path was validated on a Victus by HP Gaming Laptop 15-fb0xxx with
>board 8A3D and BIOS F.22.
>
>Signed-off-by: jR4dh3y <radheykalra901@gmail.com>
>---
> drivers/platform/x86/hp/hp-wmi.c | 56 +++++++++++++++++++++++++-------
> 1 file changed, 45 insertions(+), 11 deletions(-)
>
>diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
>index 304d9ac..b2a6d50 100644
>--- a/drivers/platform/x86/hp/hp-wmi.c
>+++ b/drivers/platform/x86/hp/hp-wmi.c
>@@ -204,6 +204,11 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst
> 	{},
> };
>
>+/* DMI Board names of Victus laptops with the Victus fan-control WMI calls. */
>+static const char * const victus_fan_control_boards[] = {
>+	"8A3D", /* Victus by HP Gaming Laptop 15-fb0xxx */
>+};
>+
> static bool is_victus_s_board;
>
> enum hp_wmi_radio {
>@@ -1715,6 +1720,22 @@ static bool is_victus_s_thermal_profile(void)
> 	return is_victus_s_board;
> }
>
>+static bool is_victus_fan_control(void)
>+{
>+	const char *board_name;
>+
>+	if (is_victus_s_thermal_profile())
>+		return true;
>+
>+	board_name = dmi_get_system_info(DMI_BOARD_NAME);
>+	if (!board_name)
>+		return false;
>+
>+	return match_string(victus_fan_control_boards,
>+			    ARRAY_SIZE(victus_fan_control_boards),
>+			    board_name) >= 0;
>+}
>+
> static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,
> 					    bool *ppab_enable,
> 					    u8 *dstate,
>@@ -2324,7 +2345,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
>
> 	switch (priv->mode) {
> 	case PWM_MODE_MAX:
>-		if (is_victus_s_thermal_profile())
>+		if (is_victus_fan_control())
> 			hp_wmi_get_fan_count_userdefine_trigger();
> 		ret = hp_wmi_fan_speed_max_set(1);
> 		if (ret < 0)
>@@ -2333,7 +2354,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
> 				      secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
> 		return 0;
> 	case PWM_MODE_MANUAL:
>-		if (!is_victus_s_thermal_profile())
>+		if (!is_victus_fan_control())
> 			return -EOPNOTSUPP;
> 		ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv));
> 		if (ret < 0)
>@@ -2342,7 +2363,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
> 				      secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
> 		return 0;
> 	case PWM_MODE_AUTO:
>-		if (is_victus_s_thermal_profile()) {
>+		if (is_victus_fan_control()) {
> 			hp_wmi_get_fan_count_userdefine_trigger();
> 			ret = hp_wmi_fan_speed_max_reset(priv);
> 		} else {
>@@ -2366,7 +2387,7 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data,
> {
> 	switch (type) {
> 	case hwmon_pwm:
>-		if (attr == hwmon_pwm_input && !is_victus_s_thermal_profile())
>+		if (attr == hwmon_pwm_input && !is_victus_fan_control())
> 			return 0;
> 		return 0644;
> 	case hwmon_fan:
>@@ -2404,10 +2425,13 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> 		return 0;
> 	case hwmon_pwm:
> 		if (attr == hwmon_pwm_input) {
>-			if (!is_victus_s_thermal_profile())
>+			if (!is_victus_fan_control())
> 				return -EOPNOTSUPP;
>
>-			rpm = hp_wmi_get_fan_speed_victus_s(channel);
>+			if (is_victus_s_thermal_profile())
>+				rpm = hp_wmi_get_fan_speed_victus_s(channel);
>+			else
>+				rpm = hp_wmi_get_fan_speed(channel);
> 			if (rpm < 0)
> 				return rpm;
> 			*val = rpm_to_pwm(rpm / 100, priv);
>@@ -2438,7 +2462,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> 	switch (type) {
> 	case hwmon_pwm:
> 		if (attr == hwmon_pwm_input) {
>-			if (!is_victus_s_thermal_profile())
>+			if (!is_victus_fan_control())
> 				return -EOPNOTSUPP;
> 			/* PWM input is invalid when not in manual mode */
> 			if (priv->mode != PWM_MODE_MANUAL)
>@@ -2455,13 +2479,16 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> 			priv->mode = PWM_MODE_MAX;
> 			return hp_wmi_apply_fan_settings(priv);
> 		case PWM_MODE_MANUAL:
>-			if (!is_victus_s_thermal_profile())
>+			if (!is_victus_fan_control())
> 				return -EOPNOTSUPP;
> 			/*
> 			 * When switching to manual mode, set fan speed to
> 			 * current RPM values to ensure a smooth transition.
> 			 */
>-			rpm = hp_wmi_get_fan_speed_victus_s(channel);
>+			if (is_victus_s_thermal_profile())
>+				rpm = hp_wmi_get_fan_speed_victus_s(channel);
>+			else
>+				rpm = hp_wmi_get_fan_speed(channel);
> 			if (rpm < 0)
> 				return rpm;
> 			priv->pwm = rpm_to_pwm(rpm / 100, priv);
>@@ -2519,9 +2546,16 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv)
> 	/* Default behaviour on hwmon init is automatic mode */
> 	priv->mode = PWM_MODE_AUTO;
>
>-	/* Bypass all non-Victus S devices */
>-	if (!is_victus_s_thermal_profile())
>+	/* Bypass devices without the Victus fan-control WMI calls. */
>+	if (!is_victus_fan_control())
>+		return 0;
>+
>+	if (!is_victus_s_thermal_profile()) {
>+		priv->min_rpm = 26;
>+		priv->max_rpm = 58;
>+		priv->gpu_delta = 3;
> 		return 0;
>+	}
>
> 	ret = hp_wmi_perform_query(HPWMI_VICTUS_S_GET_FAN_TABLE_QUERY,
> 				   HPWMI_GM, &fan_data, 4, sizeof(fan_data));
>-- 
>2.54.0
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control
  2026-05-07 19:15 ` Krishna Chomal
@ 2026-05-07 20:13   ` Radhey Kalra
  0 siblings, 0 replies; 5+ messages in thread
From: Radhey Kalra @ 2026-05-07 20:13 UTC (permalink / raw)
  To: krishna.chomal108; +Cc: platform-driver-x86, ilpo.jarvinen, hansg, Radhey Kalra

Hi Ilpo, Krishna,

Thanks for the review.

Ilpo: understood about the Signed-off-by. I will use my real name.

I agree that v1 added an ad-hoc second DMI table and spread the model
choice into the hwmon code. I will rework this so the DMI .driver_data
describes the board features, including the fan-control operations and
limits. The hwmon path will use that data instead of checking another DMI
table, and unsupported operations will return -EOPNOTSUPP.

Krishna: thanks for pointing me at 9d317a54e46d ("platform/x86: hp-wmi:
fix fan table parsing"). My statement about the Victus S fan table came
from testing the 7.0.3 CachyOS/DKMS tree before accounting for that fix.
I will retest 8A3D with mainline including that commit. If the fan table
works there, v2 will use the table-derived limits. If it still fails, I
will include the actual WMI failure/parsing details and keep any fallback
limits in .driver_data rather than hardcoding them in the hwmon path.

Thanks,
Radhey Kalra

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control
  2026-05-07 10:13 [PATCH] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control jR4dh3y
  2026-05-07 11:20 ` Ilpo Järvinen
  2026-05-07 19:15 ` Krishna Chomal
@ 2026-05-16  9:05 ` Radhey Kalra
  2 siblings, 0 replies; 5+ messages in thread
From: Radhey Kalra @ 2026-05-16  9:05 UTC (permalink / raw)
  To: platform-driver-x86; +Cc: ilpo.jarvinen, krishna.chomal108, hansg, Radhey Kalra

HP Victus 15-fb0xxx board 8A3D exposes the Victus fan table and accepts
the existing Victus fan-speed WMI control path. Add it as a
fan-control-only board so the hwmon PWM interface is enabled without
forcing Victus S thermal-profile handling on this model.

Extend the DMI .driver_data from thermal-profile-only parameters to board
parameters containing thermal-profile and fan-control data. Existing table
entries keep both thermal and fan-control data, while 8A3D only selects
the fan-control profile.

The 8A3D fan table starts with a valid CPU row whose GPU RPM field is
zero. Derive gpu_delta from the first row with non-zero GPU RPM instead of
always using row 0.

Tested on a Victus by HP Gaming Laptop 15-fb0xxx, board 8A3D, BIOS F.22.
The fan table query succeeded and returned rows including cpu=23/gpu=0
followed by cpu=24/gpu=22. With 8A3D enabled in the fan-control path, pwm1
and pwm1_enable were exposed through hwmon.

Signed-off-by: Radhey Kalra <radheykalra901@gmail.com>
---
Changes in v2:
- Use real name in Signed-off-by.
- Replace the ad-hoc fan-control DMI table with board data in .driver_data.
- Keep 8A3D fan-control-only instead of enabling Victus S thermal profiles.
- Use the fixed fan-table parser path and derive gpu_delta from the first non-zero GPU row.

 drivers/platform/x86/hp/hp-wmi.c | 199 +++++++++++++++++++++----------
 1 file changed, 137 insertions(+), 62 deletions(-)

diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index 6950bec..7435484 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -133,11 +133,50 @@ static const struct thermal_profile_params omen_v1_no_ec_thermal_params = {
 	.ec_tp_offset	= HP_NO_THERMAL_PROFILE_OFFSET,
 };
 
-/*
- * A generic pointer for the currently-active board's thermal profile
- * parameters.
- */
-static struct thermal_profile_params *active_thermal_profile_params;
+static const struct thermal_profile_params *active_thermal_profile_params;
+
+struct hp_wmi_fan_profile_params {
+	int (*get_fan_speed)(int fan);
+	bool fan_table;
+};
+
+struct hp_wmi_board_params {
+	const struct thermal_profile_params *thermal_profile;
+	const struct hp_wmi_fan_profile_params *fan_profile;
+};
+
+static int hp_wmi_get_fan_speed_victus_s(int fan);
+
+static const struct hp_wmi_fan_profile_params victus_s_fan_profile_params = {
+	.get_fan_speed	= hp_wmi_get_fan_speed_victus_s,
+	.fan_table	= true,
+};
+
+static const struct hp_wmi_board_params victus_s_board_params = {
+	.thermal_profile	= &victus_s_thermal_params,
+	.fan_profile		= &victus_s_fan_profile_params,
+};
+
+static const struct hp_wmi_board_params omen_v1_board_params = {
+	.thermal_profile	= &omen_v1_thermal_params,
+	.fan_profile		= &victus_s_fan_profile_params,
+};
+
+static const struct hp_wmi_board_params omen_v1_legacy_board_params = {
+	.thermal_profile	= &omen_v1_legacy_thermal_params,
+	.fan_profile		= &victus_s_fan_profile_params,
+};
+
+static const struct hp_wmi_board_params omen_v1_no_ec_board_params = {
+	.thermal_profile	= &omen_v1_no_ec_thermal_params,
+	.fan_profile		= &victus_s_fan_profile_params,
+};
+
+static const struct hp_wmi_board_params victus_8a3d_board_params = {
+	.fan_profile		= &victus_s_fan_profile_params,
+};
+
+static const struct hp_wmi_board_params *active_board_params;
 
 /* DMI board names of devices that should use the omen specific path for
  * thermal profiles.
@@ -187,71 +226,75 @@ static const char * const victus_thermal_profile_boards[] = {
 	"8A25",
 };
 
-/* DMI Board names of Victus 16-r and Victus 16-s laptops */
-static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst = {
+/* DMI board-specific feature data for Omen and Victus laptops. */
+static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = {
+	{
+		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8A3D") },
+		.driver_data = (void *)&victus_8a3d_board_params,
+	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8A44") },
-		.driver_data = (void *)&omen_v1_legacy_thermal_params,
+		.driver_data = (void *)&omen_v1_legacy_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8A4D") },
-		.driver_data = (void *)&omen_v1_legacy_thermal_params,
+		.driver_data = (void *)&omen_v1_legacy_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BAB") },
-		.driver_data = (void *)&omen_v1_thermal_params,
+		.driver_data = (void *)&omen_v1_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") },
-		.driver_data = (void *)&victus_s_thermal_params,
+		.driver_data = (void *)&victus_s_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BC2") },
-		.driver_data = (void *)&omen_v1_thermal_params,
+		.driver_data = (void *)&omen_v1_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BCA") },
-		.driver_data = (void *)&omen_v1_thermal_params,
+		.driver_data = (void *)&omen_v1_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BCD") },
-		.driver_data = (void *)&omen_v1_thermal_params,
+		.driver_data = (void *)&omen_v1_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BD4") },
-		.driver_data = (void *)&victus_s_thermal_params,
+		.driver_data = (void *)&victus_s_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8BD5") },
-		.driver_data = (void *)&victus_s_thermal_params,
+		.driver_data = (void *)&victus_s_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8C76") },
-		.driver_data = (void *)&omen_v1_thermal_params,
+		.driver_data = (void *)&omen_v1_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8C77") },
-		.driver_data = (void *)&omen_v1_thermal_params,
+		.driver_data = (void *)&omen_v1_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8C78") },
-		.driver_data = (void *)&omen_v1_thermal_params,
+		.driver_data = (void *)&omen_v1_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8C99") },
-		.driver_data = (void *)&victus_s_thermal_params,
+		.driver_data = (void *)&victus_s_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8C9C") },
-		.driver_data = (void *)&victus_s_thermal_params,
+		.driver_data = (void *)&victus_s_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") },
-		.driver_data = (void *)&omen_v1_no_ec_thermal_params,
+		.driver_data = (void *)&omen_v1_no_ec_board_params,
 	},
 	{
 		.matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") },
-		.driver_data = (void *)&omen_v1_no_ec_thermal_params,
+		.driver_data = (void *)&omen_v1_no_ec_board_params,
 	},
 	{},
 };
@@ -1773,6 +1816,38 @@ static bool is_victus_s_thermal_profile(void)
 	return is_victus_s_board;
 }
 
+static const struct hp_wmi_fan_profile_params *hp_wmi_fan_profile(void)
+{
+	if (!active_board_params)
+		return NULL;
+
+	return active_board_params->fan_profile;
+}
+
+static bool hp_wmi_fan_control_supported(void)
+{
+	const struct hp_wmi_fan_profile_params *params = hp_wmi_fan_profile();
+
+	return params && params->get_fan_speed;
+}
+
+static bool hp_wmi_fan_table_supported(void)
+{
+	const struct hp_wmi_fan_profile_params *params = hp_wmi_fan_profile();
+
+	return params && params->fan_table;
+}
+
+static int hp_wmi_get_active_fan_speed(int fan)
+{
+	const struct hp_wmi_fan_profile_params *params = hp_wmi_fan_profile();
+
+	if (!params || !params->get_fan_speed)
+		return -EOPNOTSUPP;
+
+	return params->get_fan_speed(fan);
+}
+
 static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,
 					    bool *ppab_enable,
 					    u8 *dstate,
@@ -1908,7 +1983,7 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil
 
 static int platform_profile_victus_s_set_ec(enum platform_profile_option profile)
 {
-	struct thermal_profile_params *params;
+	const struct thermal_profile_params *params;
 	bool gpu_ctgp_enable, gpu_ppab_enable;
 	u8 gpu_dstate; /* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */
 	int err, tp;
@@ -2384,7 +2459,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
 
 	switch (priv->mode) {
 	case PWM_MODE_MAX:
-		if (is_victus_s_thermal_profile()) {
+		if (hp_wmi_fan_control_supported()) {
 			ret = hp_wmi_get_fan_count_userdefine_trigger();
 			if (ret < 0)
 				return ret;
@@ -2396,7 +2471,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
 				 secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
 		return 0;
 	case PWM_MODE_MANUAL:
-		if (!is_victus_s_thermal_profile())
+		if (!hp_wmi_fan_control_supported())
 			return -EOPNOTSUPP;
 		ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv));
 		if (ret < 0)
@@ -2405,7 +2480,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
 				 secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
 		return 0;
 	case PWM_MODE_AUTO:
-		if (is_victus_s_thermal_profile()) {
+		if (hp_wmi_fan_control_supported()) {
 			ret = hp_wmi_get_fan_count_userdefine_trigger();
 			if (ret < 0)
 				return ret;
@@ -2429,12 +2504,12 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data,
 {
 	switch (type) {
 	case hwmon_pwm:
-		if (attr == hwmon_pwm_input && !is_victus_s_thermal_profile())
+		if (attr == hwmon_pwm_input && !hp_wmi_fan_control_supported())
 			return 0;
 		return 0644;
 	case hwmon_fan:
-		if (is_victus_s_thermal_profile()) {
-			if (hp_wmi_get_fan_speed_victus_s(channel) >= 0)
+		if (hp_wmi_fan_control_supported()) {
+			if (hp_wmi_get_active_fan_speed(channel) >= 0)
 				return 0444;
 		} else {
 			if (hp_wmi_get_fan_speed(channel) >= 0)
@@ -2458,8 +2533,8 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 	priv = dev_get_drvdata(dev);
 	switch (type) {
 	case hwmon_fan:
-		if (is_victus_s_thermal_profile())
-			ret = hp_wmi_get_fan_speed_victus_s(channel);
+		if (hp_wmi_fan_control_supported())
+			ret = hp_wmi_get_active_fan_speed(channel);
 		else
 			ret = hp_wmi_get_fan_speed(channel);
 		if (ret < 0)
@@ -2468,10 +2543,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 		return 0;
 	case hwmon_pwm:
 		if (attr == hwmon_pwm_input) {
-			if (!is_victus_s_thermal_profile())
+			if (!hp_wmi_fan_control_supported())
 				return -EOPNOTSUPP;
 
-			rpm = hp_wmi_get_fan_speed_victus_s(channel);
+			rpm = hp_wmi_get_active_fan_speed(channel);
 			if (rpm < 0)
 				return rpm;
 			*val = rpm_to_pwm(rpm / 100, priv);
@@ -2505,7 +2580,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
 	switch (type) {
 	case hwmon_pwm:
 		if (attr == hwmon_pwm_input) {
-			if (!is_victus_s_thermal_profile())
+			if (!hp_wmi_fan_control_supported())
 				return -EOPNOTSUPP;
 			/* PWM input is invalid when not in manual mode */
 			if (priv->mode != PWM_MODE_MANUAL)
@@ -2522,13 +2597,13 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
 			priv->mode = PWM_MODE_MAX;
 			return hp_wmi_apply_fan_settings(priv);
 		case PWM_MODE_MANUAL:
-			if (!is_victus_s_thermal_profile())
+			if (!hp_wmi_fan_control_supported())
 				return -EOPNOTSUPP;
 			/*
 			 * When switching to manual mode, set fan speed to
 			 * current RPM values to ensure a smooth transition.
 			 */
-			rpm = hp_wmi_get_fan_speed_victus_s(channel);
+			rpm = hp_wmi_get_active_fan_speed(channel);
 			if (rpm < 0)
 				return rpm;
 			priv->pwm = rpm_to_pwm(rpm / 100, priv);
@@ -2589,13 +2664,14 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv)
 	u8 min_rpm, max_rpm;
 	u8 cpu_rpm, gpu_rpm, noise_db;
 	int gpu_delta, i, num_entries, ret;
+	bool gpu_delta_found = false;
 	size_t header_size, entry_size;
 
 	/* Default behaviour on hwmon init is automatic mode */
 	priv->mode = PWM_MODE_AUTO;
 
-	/* Bypass all non-Victus S devices */
-	if (!is_victus_s_thermal_profile())
+	/* Bypass devices without fan control support. */
+	if (!hp_wmi_fan_table_supported())
 		return 0;
 
 	ret = hp_wmi_perform_query(HPWMI_VICTUS_S_GET_FAN_TABLE_QUERY,
@@ -2629,12 +2705,15 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv)
 			min_rpm = cpu_rpm;
 		if (cpu_rpm > max_rpm)
 			max_rpm = cpu_rpm;
+		if (!gpu_delta_found && gpu_rpm > 0) {
+			gpu_delta = gpu_rpm - cpu_rpm;
+			gpu_delta_found = true;
+		}
 	}
 
-	if (min_rpm == U8_MAX || max_rpm == 0)
+	if (min_rpm == U8_MAX || max_rpm == 0 || !gpu_delta_found)
 		return -EINVAL;
 
-	gpu_delta = fan_table->entries[0].gpu_rpm - fan_table->entries[0].cpu_rpm;
 	priv->min_rpm = min_rpm;
 	priv->max_rpm = max_rpm;
 	priv->gpu_delta = gpu_delta;
@@ -2677,26 +2756,25 @@ static int hp_wmi_hwmon_init(void)
 	return 0;
 }
 
-static void __init setup_active_thermal_profile_params(void)
+static void __init setup_active_board_params(void)
 {
 	const struct dmi_system_id *id;
 
-	/*
-	 * Currently only victus_s devices use the
-	 * active_thermal_profile_params
-	 */
-	id = dmi_first_match(victus_s_thermal_profile_boards);
+	id = dmi_first_match(hp_wmi_feature_boards);
 	if (id) {
-		/*
-		 * Marking this boolean is required to ensure that
-		 * is_victus_s_thermal_profile() behaves like a valid
-		 * wrapper.
-		 */
-		is_victus_s_board = true;
-		active_thermal_profile_params = id->driver_data;
-		if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) {
-			pr_warn("Unknown EC layout for board %s. Thermal profile readback will be disabled. Please report this to platform-driver-x86@vger.kernel.org\n",
-				dmi_get_system_info(DMI_BOARD_NAME));
+		active_board_params = id->driver_data;
+		active_thermal_profile_params = active_board_params->thermal_profile;
+		if (active_thermal_profile_params) {
+			/*
+			 * Marking this boolean is required to ensure that
+			 * is_victus_s_thermal_profile() behaves like a valid
+			 * wrapper.
+			 */
+			is_victus_s_board = true;
+			if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) {
+				pr_warn("Unknown EC layout for board %s. Thermal profile readback will be disabled. Please report this to platform-driver-x86@vger.kernel.org\n",
+					dmi_get_system_info(DMI_BOARD_NAME));
+			}
 		}
 	}
 }
@@ -2728,11 +2806,8 @@ static int __init hp_wmi_init(void)
 			goto err_destroy_input;
 		}
 
-		/*
-		 * Setup active board's thermal profile parameters before
-		 * starting platform driver probe.
-		 */
-		setup_active_thermal_profile_params();
+		/* Setup active board feature data before starting platform driver probe. */
+		setup_active_board_params();
 		err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
 		if (err)
 			goto err_unregister_device;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-05-16  9:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 10:13 [PATCH] platform/x86: hp-wmi: Add Victus 15-fb0xxx fan control jR4dh3y
2026-05-07 11:20 ` Ilpo Järvinen
2026-05-07 19:15 ` Krishna Chomal
2026-05-07 20:13   ` Radhey Kalra
2026-05-16  9:05 ` [PATCH v2] " Radhey Kalra

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.