mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
platform/x86: hp-wmi: Add GPU MUX switch support
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ı <hello@kursatabayli.dev> Link: https://patch.msgid.link/20260723172734.18361-1-hello@kursatabayli.dev [ij: add kstrtox.h] Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
parent
caf8342512
commit
59f586eb93
|
|
@ -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>
|
||||
|
|
@ -23,6 +25,7 @@
|
|||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -58,6 +61,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 +390,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 {
|
||||
|
|
@ -1171,12 +1181,134 @@ 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)
|
||||
return ret;
|
||||
if (ret > 0)
|
||||
return -EINVAL;
|
||||
|
||||
*supported = HPWMI_MUX_LEGACY_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
@ -1185,6 +1317,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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user