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.