Re: [PATCH v4] platform/x86: hp-wmi: Add GPU MUX switch support

Ilpo Järvinen <[email protected]>
Newsgroups org.kernel.vger.platform-driver-x86,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On Sat, 11 Jul 2026, Kürşat Abaylı wrote:

> Add support for querying and switching the graphics MUX mode on HP
> systems via WMI. This introduces the 'gpu_mux_mode' sysfs attribute
> under the hp-wmi platform device, allowing userspace tools to check
> and safely switch between available graphics modes (e.g., UMA, Hybrid,
> Discrete).
> 
> The hardware capabilities mask is primarily read using the modern
> 128-byte System Design Data query. However, to ensure backward
> compatibility with older models, a fallback mechanism is implemented.
> By mirroring the behavior of the Windows Omen Gaming Hub software, if
> the modern query fails but the MUX WMI endpoint (0x52) responds
> successfully to a read request, the driver defaults to a standard
> Hybrid + Discrete support mask (0x06).
> 
> Signed-off-by: Kürşat Abaylı <[email protected]>
> ---
> Changes in v4:
>  - Removed empty lines between function calls and error handling.
> 
> Changes in v3:
>  - Rebased for the for-next branch (no functional changes).
> 
> Changes in v2:
>  - Replaced hardcoded bitmask with GENMASK(6, 0).
>  - Added specific defines for MUX modes instead of using raw BIT() macros.
>  - Initialized arrays with {} instead of { 0 }.
>  - Fixed reverse xmas-tree ordering for local variable declarations.
>  - Removed empty lines between function calls and their return value checks.
>  - Split ternary operators into standard if-statements for better readability.
>  - Added missing <linux/array_size.h> include.
>  - Removed unnecessary cast in hp_wmi_set_mux_mode.
> ---
>  drivers/platform/x86/hp/hp-wmi.c | 134 +++++++++++++++++++++++++++++++
>  1 file changed, 134 insertions(+)
> 
> diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
> index 34c9b941bdd8..b5486caaec8a 100644
> --- a/drivers/platform/x86/hp/hp-wmi.c
> +++ b/drivers/platform/x86/hp/hp-wmi.c
> @@ -14,6 +14,8 @@
>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>  
>  #include <linux/acpi.h>
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
>  #include <linux/cleanup.h>
>  #include <linux/compiler_attributes.h>
>  #include <linux/dmi.h>
> @@ -58,6 +60,12 @@ enum hp_ec_offsets {
>  #define HP_FAN_SPEED_AUTOMATIC	 0x00
>  #define HP_POWER_LIMIT_DEFAULT	 0x00
>  #define HP_POWER_LIMIT_NO_CHANGE 0xFF
> +#define HPWMI_MUX_MODE_UMA		BIT(0)
> +#define HPWMI_MUX_MODE_HYBRID		BIT(1)
> +#define HPWMI_MUX_MODE_DISCRETE		BIT(2)
> +#define HPWMI_MUX_MODE_OPTIMUS		BIT(3)
> +#define HPWMI_MUX_MODE_MASK		GENMASK(6, 0)
> +#define HPWMI_MUX_LEGACY_MASK		(HPWMI_MUX_MODE_HYBRID | HPWMI_MUX_MODE_DISCRETE)
>  
>  #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
>  
> @@ -381,6 +389,7 @@ enum hp_wmi_commandtype {
>  	HPWMI_POSTCODEERROR_QUERY	= 0x2a,
>  	HPWMI_SYSTEM_DEVICE_MODE	= 0x40,
>  	HPWMI_THERMAL_PROFILE_QUERY	= 0x4c,
> +	HPWMI_GRAPHICS_MUX_QUERY	= 0x52,
>  };
>  
>  struct victus_power_limits {
> @@ -1173,12 +1182,136 @@ static int camera_shutter_input_setup(void)
>  	return err;
>  }
>  
> +static const u8 mux_bitmask_map[] = {
> +	[0] = HPWMI_MUX_MODE_HYBRID,
> +	[1] = HPWMI_MUX_MODE_DISCRETE,
> +	[2] = HPWMI_MUX_MODE_OPTIMUS,
> +	[3] = HPWMI_MUX_MODE_UMA,
> +};
> +
> +static int hp_wmi_get_mux_supported_modes(u8 *supported)
> +{
> +	u8 legacy_buffer[4] = {};
> +	u8 buffer[128] = {};
> +	u32 req_packet = 0;
> +	int ret;
> +
> +	if (!supported)
> +		return -EINVAL;
> +
> +	/* Try modern BIOS design data query (128-byte buffer) */
> +	ret = hp_wmi_perform_query(HPWMI_GET_SYSTEM_DESIGN_DATA, HPWMI_GM,
> +				   buffer, zero_if_sup(req_packet), sizeof(buffer));
> +	if (ret == 0) {
> +		*supported = buffer[7];
> +		return 0;
> +	}
> +
> +	/*
> +	 * (Fallback): Legacy BIOS behavior based on Omen Gaming Hub.
> +	 * If the modern query is not supported, check if the MUX query endpoint
> +	 * responds to a read request. If it succeeds, the hardware has MUX
> +	 * capability but lacks the mode map, defaulting to Hybrid + Discrete.
> +	 */
> +	ret = hp_wmi_perform_query(HPWMI_GRAPHICS_MUX_QUERY, HPWMI_READ,
> +				   legacy_buffer, sizeof(legacy_buffer), 0);
> +	if (ret == 0) {
> +		*supported = HPWMI_MUX_LEGACY_MASK;
> +		return 0;
> +	}
> +
> +	if (ret < 0)
> +		return ret;
> +	return -EINVAL;

Please reverse the logic here so that it's similar to 
hp_wmi_get_mux_mode(). We try to keep the non-error path on the top-level 
as it will be easier to follow the logic then.

> +}
> +
> +static int hp_wmi_get_mux_mode(u8 *mode)
> +{
> +	u8 buffer[4] = {};
> +	int ret;
> +
> +	if (!mode)
> +		return -EINVAL;
> +
> +	ret = hp_wmi_perform_query(HPWMI_GRAPHICS_MUX_QUERY, HPWMI_READ,
> +				   buffer, sizeof(buffer), sizeof(buffer));
> +	if (ret < 0)
> +		return ret;
> +	if (ret > 0)
> +		return -EINVAL;
> +
> +	/* Mask the highest bit, which might be used as a BIOS status flag */
> +	*mode = buffer[0] & HPWMI_MUX_MODE_MASK;
> +
> +	return 0;
> +}
> +
> +static int hp_wmi_set_mux_mode(u8 mode)
> +{
> +	u8 buffer[4] = { mode, 0x00, 0x00, 0x00 };
> +	int ret;
> +
> +	ret = hp_wmi_perform_query(HPWMI_GRAPHICS_MUX_QUERY, HPWMI_WRITE,
> +				   buffer, sizeof(buffer), sizeof(buffer));
> +	if (ret < 0)
> +		return ret;
> +	if (ret > 0)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static ssize_t gpu_mux_mode_show(struct device *dev,
> +				 struct device_attribute *attr,
> +				 char *buf)
> +{
> +	u8 mode;
> +	int ret;
> +
> +	ret = hp_wmi_get_mux_mode(&mode);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "%u\n", mode);
> +}
> +
> +static ssize_t gpu_mux_mode_store(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf,
> +				  size_t count)
> +{
> +	u32 requested;
> +	u8 supported;
> +	int ret;
> +
> +	ret = kstrtou32(buf, 0, &requested);
> +	if (ret)
> +		return ret;
> +	if (requested >= ARRAY_SIZE(mux_bitmask_map))
> +		return -EINVAL;
> +
> +	ret = hp_wmi_get_mux_supported_modes(&supported);
> +	if (ret)
> +		return ret;
> +
> +	/* Verify if the requested mode is allowed by the hardware mask */
> +	if (!(supported & mux_bitmask_map[requested]))
> +		return -EOPNOTSUPP;
> +
> +	ret = hp_wmi_set_mux_mode(requested);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
>  static DEVICE_ATTR_RO(display);
>  static DEVICE_ATTR_RO(hddtemp);
>  static DEVICE_ATTR_RW(als);
>  static DEVICE_ATTR_RO(dock);
>  static DEVICE_ATTR_RO(tablet);
>  static DEVICE_ATTR_RW(postcode);
> +static DEVICE_ATTR_RW(gpu_mux_mode);
>  
>  static struct attribute *hp_wmi_attrs[] = {
>  	&dev_attr_display.attr,
> @@ -1187,6 +1320,7 @@ static struct attribute *hp_wmi_attrs[] = {
>  	&dev_attr_dock.attr,
>  	&dev_attr_tablet.attr,
>  	&dev_attr_postcode.attr,
> +	&dev_attr_gpu_mux_mode.attr,
>  	NULL,
>  };
>  ATTRIBUTE_GROUPS(hp_wmi);
> 

-- 
 i.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.