From 0afaa446a07db0ded4c032511b35485bd12ff80f Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Sat, 17 Dec 2022 18:14:04 +0100 Subject: [PATCH] asus-linux Signed-off-by: Jan200101 --- .../ABI/testing/sysfs-platform-asus-wmi | 41 ++ drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 4 + drivers/hid/amd-sfh-hid/amd_sfh_pcie.h | 1 + .../hid_descriptor/amd_sfh_hid_desc.c | 27 + .../hid_descriptor/amd_sfh_hid_desc.h | 8 + .../hid_descriptor/amd_sfh_hid_report_desc.h | 19 + drivers/platform/x86/asus-nb-wmi.c | 14 +- drivers/platform/x86/asus-wmi.c | 677 ++++++++++-------- include/linux/platform_data/x86/asus-wmi.h | 10 + 9 files changed, 503 insertions(+), 298 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 04885738cf15..a77a004a1baa 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -57,3 +57,44 @@ Description: * 0 - default, * 1 - overboost, * 2 - silent + +What: /sys/devices/platform//gpu_mux_mode +Date: Aug 2022 +KernelVersion: 6.1 +Contact: "Luke Jones" +Description: + Switch the GPU hardware MUX mode. Laptops with this feature can + can be toggled to boot with only the dGPU (discrete mode) or in + standard Optimus/Hybrid mode. On switch a reboot is required: + + * 0 - Discrete GPU, + * 1 - Optimus/Hybrid, + +What: /sys/devices/platform//dgpu_disable +Date: Aug 2022 +KernelVersion: 5.17 +Contact: "Luke Jones" +Description: + Disable discrete GPU: + * 0 - Enable dGPU, + * 1 - Disable dGPU + +What: /sys/devices/platform//egpu_enable +Date: Aug 2022 +KernelVersion: 5.17 +Contact: "Luke Jones" +Description: + Enable the external GPU paired with ROG X-Flow laptops. + Toggling this setting will also trigger ACPI to disable the dGPU: + + * 0 - Disable, + * 1 - Enable + +What: /sys/devices/platform//panel_od +Date: Aug 2022 +KernelVersion: 5.17 +Contact: "Luke Jones" +Description: + Enable an LCD response-time boost to reduce or remove ghosting: + * 0 - Disable, + * 1 - Enable diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index 47774b9ab3de..a03f0968e82f 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -27,6 +27,7 @@ #define ACEL_EN BIT(0) #define GYRO_EN BIT(1) #define MAGNO_EN BIT(2) +#define KBGUARD_EN BIT(15) #define HPD_EN BIT(16) #define ALS_EN BIT(19) @@ -233,6 +234,9 @@ int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id) if (HPD_EN & activestatus) sensor_id[num_of_sensors++] = HPD_IDX; + if (KBGUARD_EN & activestatus) + sensor_id[num_of_sensors++] = KBGUARD_IDX; + return num_of_sensors; } diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h index dfb7cabd82ef..5fa15eed43f3 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h @@ -23,6 +23,7 @@ #define V2_STATUS 0x2 #define HPD_IDX 16 +#define KBGUARD_IDX 15 #define SENSOR_DISCOVERY_STATUS_MASK GENMASK(5, 3) #define SENSOR_DISCOVERY_STATUS_SHIFT 3 diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c index f9a8c02d5a7b..06487eb75dc8 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c @@ -57,6 +57,11 @@ static int get_report_descriptor(int sensor_idx, u8 *rep_desc) memcpy(rep_desc, hpd_report_descriptor, sizeof(hpd_report_descriptor)); break; + case KBGUARD_IDX: /* kbguard ? */ + memset(rep_desc, 0, sizeof(kbguard_report_descriptor)); + memcpy(rep_desc, kbguard_report_descriptor, + sizeof(kbguard_report_descriptor)); + break; default: break; } @@ -116,6 +121,16 @@ static u32 get_descr_sz(int sensor_idx, int descriptor_name) return sizeof(struct hpd_feature_report); } break; + case KBGUARD_IDX: + switch (descriptor_name) { + case descr_size: + return sizeof(kbguard_report_descriptor); + case input_size: + return sizeof(struct kbguard_input_report); + case feature_size: + return sizeof(struct kbguard_feature_report); + } + break; default: break; @@ -139,6 +154,7 @@ static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report) struct gyro_feature_report gyro_feature; struct magno_feature_report magno_feature; struct hpd_feature_report hpd_feature; + struct kbguard_feature_report kbguard_feature; struct als_feature_report als_feature; u8 report_size = 0; @@ -186,6 +202,11 @@ static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report) memcpy(feature_report, &hpd_feature, sizeof(hpd_feature)); report_size = sizeof(hpd_feature); break; + case KBGUARD_IDX: /* auto disable keyboard when flip out */ + get_common_features(&kbguard_feature.common_property, report_id); + memcpy(feature_report, &kbguard_feature, sizeof(kbguard_feature)); + report_size = sizeof(kbguard_feature); + break; default: break; @@ -211,6 +232,7 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id, struct accel3_input_report acc_input; struct gyro_input_report gyro_input; struct hpd_input_report hpd_input; + struct kbguard_input_report kbguard_input; struct als_input_report als_input; struct hpd_status hpdstatus; u8 report_size = 0; @@ -263,6 +285,11 @@ static u8 get_input_report(u8 current_index, int sensor_idx, int report_id, report_size = sizeof(hpd_input); memcpy(input_report, &hpd_input, sizeof(hpd_input)); break; + case KBGUARD_IDX: /* kb guard */ + get_common_inputs(&kbguard_input.common_property, report_id); + report_size = sizeof(kbguard_input); + memcpy(input_report, &kbguard_input, sizeof(kbguard_input)); +break; default: break; } diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h index ebd55675eb62..2f2ba9a0cfbc 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h @@ -111,4 +111,12 @@ struct hpd_input_report { u8 human_presence; } __packed; +struct kbguard_feature_report { + struct common_feature_property common_property; +} __packed; + +struct kbguard_input_report { + struct common_input_property common_property; +} __packed; + #endif diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h index 697f2791ea9c..7a62fcec2c73 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_report_desc.h @@ -644,6 +644,25 @@ static const u8 als_report_descriptor[] = { 0xC0 /* HID end collection */ }; + +static const u8 kbguard_report_descriptor[] = { +0x06, 0x43, 0xFF, // Usage Page (Vendor Defined 0xFF43) +0x0A, 0x02, 0x02, // Usage (0x0202) +0xA1, 0x01, // Collection (Application) +0x85, 0x11, // Report ID (17) +0x15, 0x00, // Logical Minimum (0) +0x25, 0x01, // Logical Maximum (1) +0x35, 0x00, // Physical Minimum (0) +0x45, 0x01, // Physical Maximum (1) +0x65, 0x00, // Unit (None) +0x55, 0x00, // Unit Exponent (0) +0x75, 0x01, // Report Size (1) +0x95, 0x98, // Report Count (-104) +0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) +0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) +0xC1, 0x00, // End Collection +}; + /* BIOMETRIC PRESENCE*/ static const u8 hpd_report_descriptor[] = { 0x05, 0x20, /* Usage page */ diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index d9e7cf6e4a0e..fcfe6dddd645 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -504,17 +504,8 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) else wapf = quirks->wapf; - switch (tablet_mode_sw) { - case 0: - quirks->tablet_switch_mode = asus_wmi_no_tablet_switch; - break; - case 1: - quirks->tablet_switch_mode = asus_wmi_kbd_dock_devid; - break; - case 2: - quirks->tablet_switch_mode = asus_wmi_lid_flip_devid; - break; - } + if (tablet_mode_sw != -1) + quirks->tablet_switch_mode = tablet_mode_sw; if (quirks->i8042_filter) { ret = i8042_install_filter(quirks->i8042_filter); @@ -586,6 +577,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */ { KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */ { KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */ + { KE_KEY, 0xAE, { KEY_FN_F5 } }, /* Fn+F5 fan mode on 2020+ */ { KE_KEY, 0xB3, { KEY_PROG4 } }, /* AURA */ { KE_KEY, 0xB5, { KEY_CALC } }, { KE_KEY, 0xC4, { KEY_KBDILLUMUP } }, diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index dce93187e11f..40e911467037 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -72,6 +72,7 @@ module_param(fnlock_default, bool, 0444); #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) +#define ASUS_GPU_FAN_DESC "gpu_fan" #define ASUS_FAN_DESC "cpu_fan" #define ASUS_FAN_MFUN 0x13 #define ASUS_FAN_SFUN_READ 0x06 @@ -222,19 +223,25 @@ struct asus_wmi { struct asus_rfkill gps; struct asus_rfkill uwb; + int tablet_switch_event_code; + u32 tablet_switch_dev_id; + enum fan_type fan_type; + enum fan_type gpu_fan_type; int fan_pwm_mode; + int gpu_fan_pwm_mode; int agfn_pwm; bool fan_boost_mode_available; u8 fan_boost_mode_mask; u8 fan_boost_mode; - bool egpu_enable_available; // 0 = enable - bool egpu_enable; - + bool egpu_enable_available; bool dgpu_disable_available; - bool dgpu_disable; + bool gpu_mux_mode_available; + + bool kbd_rgb_mode_available; + bool kbd_rgb_state_available; bool throttle_thermal_policy_available; u8 throttle_thermal_policy_mode; @@ -250,7 +257,6 @@ struct asus_wmi { bool battery_rsoc_available; bool panel_overdrive_available; - bool panel_overdrive; struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; @@ -487,13 +493,28 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id) } /* Input **********************************************************************/ +static void asus_wmi_tablet_sw_init(struct asus_wmi *asus, u32 dev_id, int event_code) +{ + struct device *dev = &asus->platform_device->dev; + int result; + + result = asus_wmi_get_devstate_simple(asus, dev_id); + if (result >= 0) { + input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE); + input_report_switch(asus->inputdev, SW_TABLET_MODE, result); + asus->tablet_switch_dev_id = dev_id; + asus->tablet_switch_event_code = event_code; + } else if (result == -ENODEV) { + dev_err(dev, "This device has tablet-mode-switch quirk but got ENODEV checking it. This is a bug."); + } else { + dev_err(dev, "Error checking for tablet-mode-switch: %d\n", result); + } +} static int asus_wmi_input_init(struct asus_wmi *asus) { - struct device *dev; - int err, result; - - dev = &asus->platform_device->dev; + struct device *dev = &asus->platform_device->dev; + int err; asus->inputdev = input_allocate_device(); if (!asus->inputdev) @@ -513,39 +534,13 @@ static int asus_wmi_input_init(struct asus_wmi *asus) case asus_wmi_no_tablet_switch: break; case asus_wmi_kbd_dock_devid: - result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_DOCK); - if (result >= 0) { - input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE); - input_report_switch(asus->inputdev, SW_TABLET_MODE, !result); - } else if (result != -ENODEV) { - dev_err(dev, "Error checking for keyboard-dock: %d\n", result); - } + asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_KBD_DOCK, NOTIFY_KBD_DOCK_CHANGE); break; case asus_wmi_lid_flip_devid: - result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_LID_FLIP); - if (result < 0) - asus->driver->quirks->tablet_switch_mode = asus_wmi_no_tablet_switch; - if (result >= 0) { - input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE); - input_report_switch(asus->inputdev, SW_TABLET_MODE, result); - } else if (result == -ENODEV) { - dev_err(dev, "This device has lid_flip quirk but got ENODEV checking it. This is a bug."); - } else { - dev_err(dev, "Error checking for lid-flip: %d\n", result); - } + asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_LID_FLIP, NOTIFY_LID_FLIP); break; case asus_wmi_lid_flip_rog_devid: - result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_LID_FLIP_ROG); - if (result < 0) - asus->driver->quirks->tablet_switch_mode = asus_wmi_no_tablet_switch; - if (result >= 0) { - input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE); - input_report_switch(asus->inputdev, SW_TABLET_MODE, result); - } else if (result == -ENODEV) { - dev_err(dev, "This device has lid-flip-rog quirk but got ENODEV checking it. This is a bug."); - } else { - dev_err(dev, "Error checking for lid-flip: %d\n", result); - } + asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_LID_FLIP_ROG, NOTIFY_LID_FLIP_ROG); break; } @@ -570,22 +565,14 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) /* Tablet mode ****************************************************************/ -static void lid_flip_tablet_mode_get_state(struct asus_wmi *asus) +static void asus_wmi_tablet_mode_get_state(struct asus_wmi *asus) { int result; - result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_LID_FLIP); - if (result >= 0) { - input_report_switch(asus->inputdev, SW_TABLET_MODE, result); - input_sync(asus->inputdev); - } -} - -static void lid_flip_rog_tablet_mode_get_state(struct asus_wmi *asus) -{ - int result; + if (!asus->tablet_switch_dev_id) + return; - result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_LID_FLIP_ROG); + result = asus_wmi_get_devstate_simple(asus, asus->tablet_switch_dev_id); if (result >= 0) { input_report_switch(asus->inputdev, SW_TABLET_MODE, result); input_sync(asus->inputdev); @@ -593,179 +580,267 @@ static void lid_flip_rog_tablet_mode_get_state(struct asus_wmi *asus) } /* dGPU ********************************************************************/ -static int dgpu_disable_check_present(struct asus_wmi *asus) +static ssize_t dgpu_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) { - u32 result; - int err; - - asus->dgpu_disable_available = false; - - err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_DGPU, &result); - if (err) { - if (err == -ENODEV) - return 0; - return err; - } + struct asus_wmi *asus = dev_get_drvdata(dev); + int result; - if (result & ASUS_WMI_DSTS_PRESENCE_BIT) { - asus->dgpu_disable_available = true; - asus->dgpu_disable = result & ASUS_WMI_DSTS_STATUS_BIT; - } + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_DGPU); + if (result < 0) + return result; - return 0; + return sysfs_emit(buf, "%d\n", result); } -static int dgpu_disable_write(struct asus_wmi *asus) +/* + * A user may be required to store the value twice, typcial store first, then + * rescan PCI bus to activate power, then store a second time to save correctly. + * The reason for this is that an extra code path in the ACPI is enabled when + * the device and bus are powered. + */ +static ssize_t dgpu_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - u32 retval; - u8 value; - int err; + int result, err; + u32 disable; - /* Don't rely on type conversion */ - value = asus->dgpu_disable ? 1 : 0; + struct asus_wmi *asus = dev_get_drvdata(dev); - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, value, &retval); + result = kstrtou32(buf, 10, &disable); + if (result) + return result; + + if (disable > 1) + return -EINVAL; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, disable, &result); if (err) { pr_warn("Failed to set dgpu disable: %d\n", err); return err; } - if (retval > 1) { - pr_warn("Failed to set dgpu disable (retval): 0x%x\n", retval); + if (result > 1) { + pr_warn("Failed to set dgpu disable (result): 0x%x\n", result); return -EIO; } sysfs_notify(&asus->platform_device->dev.kobj, NULL, "dgpu_disable"); - return 0; + return count; } +static DEVICE_ATTR_RW(dgpu_disable); -static ssize_t dgpu_disable_show(struct device *dev, +/* eGPU ********************************************************************/ +static ssize_t egpu_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct asus_wmi *asus = dev_get_drvdata(dev); - u8 mode = asus->dgpu_disable; + int result; - return sysfs_emit(buf, "%d\n", mode); + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU); + if (result < 0) + return result; + + return sysfs_emit(buf, "%d\n", result); } -/* - * A user may be required to store the value twice, typcial store first, then - * rescan PCI bus to activate power, then store a second time to save correctly. - * The reason for this is that an extra code path in the ACPI is enabled when - * the device and bus are powered. - */ -static ssize_t dgpu_disable_store(struct device *dev, +/* The ACPI call to enable the eGPU also disables the internal dGPU */ +static ssize_t egpu_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - bool disable; - int result; + int result, err; + u32 enable; struct asus_wmi *asus = dev_get_drvdata(dev); - result = kstrtobool(buf, &disable); - if (result) - return result; + err = kstrtou32(buf, 10, &enable); + if (err) + return err; - asus->dgpu_disable = disable; + if (enable > 1) + return -EINVAL; - result = dgpu_disable_write(asus); - if (result) - return result; + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, enable, &result); + if (err) { + pr_warn("Failed to set egpu disable: %d\n", err); + return err; + } + + if (result > 1) { + pr_warn("Failed to set egpu disable (retval): 0x%x\n", result); + return -EIO; + } + + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "egpu_enable"); return count; } +static DEVICE_ATTR_RW(egpu_enable); -static DEVICE_ATTR_RW(dgpu_disable); +/* gpu mux switch *************************************************************/ +static ssize_t gpu_mux_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + int result; -/* eGPU ********************************************************************/ -static int egpu_enable_check_present(struct asus_wmi *asus) + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX); + if (result < 0) + return result; + + return sysfs_emit(buf, "%d\n", result); +} + +static ssize_t gpu_mux_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - u32 result; - int err; + struct asus_wmi *asus = dev_get_drvdata(dev); + int result, err; + u32 optimus; - asus->egpu_enable_available = false; + err = kstrtou32(buf, 10, &optimus); + if (err) + return err; - err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_EGPU, &result); + if (optimus > 1) + return -EINVAL; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_MUX, optimus, &result); if (err) { - if (err == -ENODEV) - return 0; + dev_err(dev, "Failed to set GPU MUX mode: %d\n", err); return err; } - - if (result & ASUS_WMI_DSTS_PRESENCE_BIT) { - asus->egpu_enable_available = true; - asus->egpu_enable = result & ASUS_WMI_DSTS_STATUS_BIT; + /* !1 is considered a fail by ASUS */ + if (result != 1) { + dev_warn(dev, "Failed to set GPU MUX mode (result): 0x%x\n", result); + return -EIO; } - return 0; + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "gpu_mux_mode"); + + return count; } +static DEVICE_ATTR_RW(gpu_mux_mode); -static int egpu_enable_write(struct asus_wmi *asus) +/* TUF Laptop Keyboard RGB Modes **********************************************/ +static ssize_t kbd_rgb_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - u32 retval; - u8 value; + u32 cmd, mode, r, g, b, speed; int err; - /* Don't rely on type conversion */ - value = asus->egpu_enable ? 1 : 0; + if (sscanf(buf, "%d %d %d %d %d %d", &cmd, &mode, &r, &g, &b, &speed) != 6) + return -EINVAL; - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, value, &retval); + cmd = !!cmd; - if (err) { - pr_warn("Failed to set egpu disable: %d\n", err); - return err; - } + /* These are the known usable modes across all TUF/ROG */ + if (mode >= 12 || mode == 9) + mode = 10; - if (retval > 1) { - pr_warn("Failed to set egpu disable (retval): 0x%x\n", retval); - return -EIO; + switch (speed) { + case 0: + speed = 0xe1; + break; + case 1: + speed = 0xeb; + break; + case 2: + speed = 0xf5; + break; + default: + speed = 0xeb; } - sysfs_notify(&asus->platform_device->dev.kobj, NULL, "egpu_enable"); + err = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS, ASUS_WMI_DEVID_TUF_RGB_MODE, + cmd | (mode << 8) | (r << 16) | (g << 24), b | (speed << 8), NULL); + if (err) + return err; - return 0; + return count; } +static DEVICE_ATTR_WO(kbd_rgb_mode); -static ssize_t egpu_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t kbd_rgb_mode_index_show(struct device *device, + struct device_attribute *attr, + char *buf) { - struct asus_wmi *asus = dev_get_drvdata(dev); - bool mode = asus->egpu_enable; - - return sysfs_emit(buf, "%d\n", mode); + return sysfs_emit(buf, "%s\n", "cmd mode red green blue speed"); } +static DEVICE_ATTR_RO(kbd_rgb_mode_index); -/* The ACPI call to enable the eGPU also disables the internal dGPU */ -static ssize_t egpu_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - bool enable; - int result; - - struct asus_wmi *asus = dev_get_drvdata(dev); +static struct attribute *kbd_rgb_mode_attrs[] = { + &dev_attr_kbd_rgb_mode.attr, + &dev_attr_kbd_rgb_mode_index.attr, + NULL, +}; - result = kstrtobool(buf, &enable); - if (result) - return result; +static const struct attribute_group kbd_rgb_mode_group = { + .attrs = kbd_rgb_mode_attrs, +}; - asus->egpu_enable = enable; +/* TUF Laptop Keyboard RGB State **********************************************/ +static ssize_t kbd_rgb_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 flags, cmd, boot, awake, sleep, keyboard; + int err; - result = egpu_enable_write(asus); - if (result) - return result; + if (sscanf(buf, "%d %d %d %d %d", &cmd, &boot, &awake, &sleep, &keyboard) != 5) + return -EINVAL; - /* Ensure that the kernel status of dgpu is updated */ - result = dgpu_disable_check_present(asus); - if (result) - return result; + if (cmd) + cmd = BIT(2); + + flags = 0; + if (boot) + flags |= BIT(1); + if (awake) + flags |= BIT(3); + if (sleep) + flags |= BIT(5); + if (keyboard) + flags |= BIT(7); + + /* 0xbd is the required default arg0 for the method. Nothing happens otherwise */ + err = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS, + ASUS_WMI_DEVID_TUF_RGB_STATE, 0xbd | cmd << 8 | (flags << 16), 0, NULL); + if (err) + return err; return count; } +static DEVICE_ATTR_WO(kbd_rgb_state); -static DEVICE_ATTR_RW(egpu_enable); +static ssize_t kbd_rgb_state_index_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", "cmd boot awake sleep keyboard"); +} +static DEVICE_ATTR_RO(kbd_rgb_state_index); + +static struct attribute *kbd_rgb_state_attrs[] = { + &dev_attr_kbd_rgb_state.attr, + &dev_attr_kbd_rgb_state_index.attr, + NULL, +}; + +static const struct attribute_group kbd_rgb_state_group = { + .attrs = kbd_rgb_state_attrs, +}; + +const struct attribute_group *kbd_rgb_mode_groups[] = { + NULL, + NULL, + NULL, +}; /* Battery ********************************************************************/ @@ -803,7 +878,7 @@ static ssize_t charge_control_end_threshold_show(struct device *device, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", charge_end_threshold); + return sysfs_emit(buf, "%d\n", charge_end_threshold); } static DEVICE_ATTR_RW(charge_control_end_threshold); @@ -1085,7 +1160,12 @@ static void asus_wmi_led_exit(struct asus_wmi *asus) static int asus_wmi_led_init(struct asus_wmi *asus) { - int rv = 0, led_val; + int rv = 0, num_rgb_groups = 0, led_val; + + if (asus->kbd_rgb_mode_available) + kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group; + if (asus->kbd_rgb_state_available) + kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_state_group; asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); if (!asus->led_workqueue) @@ -1113,6 +1193,9 @@ static int asus_wmi_led_init(struct asus_wmi *asus) asus->kbd_led.brightness_get = kbd_led_get; asus->kbd_led.max_brightness = 3; + if (num_rgb_groups != 0) + asus->kbd_led.groups = kbd_rgb_mode_groups; + rv = led_classdev_register(&asus->platform_device->dev, &asus->kbd_led); if (rv) @@ -1587,84 +1670,51 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus) } /* Panel Overdrive ************************************************************/ -static int panel_od_check_present(struct asus_wmi *asus) -{ - u32 result; - int err; - - asus->panel_overdrive_available = false; - - err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_PANEL_OD, &result); - if (err) { - if (err == -ENODEV) - return 0; - return err; - } - - if (result & ASUS_WMI_DSTS_PRESENCE_BIT) { - asus->panel_overdrive_available = true; - asus->panel_overdrive = result & ASUS_WMI_DSTS_STATUS_BIT; - } - - return 0; -} - -static int panel_od_write(struct asus_wmi *asus) -{ - u32 retval; - u8 value; - int err; - - /* Don't rely on type conversion */ - value = asus->panel_overdrive ? 1 : 0; - - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PANEL_OD, value, &retval); - - if (err) { - pr_warn("Failed to set panel overdrive: %d\n", err); - return err; - } - - if (retval > 1) { - pr_warn("Failed to set panel overdrive (retval): 0x%x\n", retval); - return -EIO; - } - - sysfs_notify(&asus->platform_device->dev.kobj, NULL, "panel_od"); - - return 0; -} - static ssize_t panel_od_show(struct device *dev, struct device_attribute *attr, char *buf) { struct asus_wmi *asus = dev_get_drvdata(dev); + int result; - return sysfs_emit(buf, "%d\n", asus->panel_overdrive); + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_PANEL_OD); + if (result < 0) + return result; + + return sysfs_emit(buf, "%d\n", result); } static ssize_t panel_od_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - bool overdrive; - int result; + int result, err; + u32 overdrive; struct asus_wmi *asus = dev_get_drvdata(dev); - result = kstrtobool(buf, &overdrive); + result = kstrtou32(buf, 10, &overdrive); if (result) return result; - asus->panel_overdrive = overdrive; - result = panel_od_write(asus); + if (overdrive > 1) + return -EINVAL; - if (result) - return result; + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PANEL_OD, overdrive, &result); + + if (err) { + pr_warn("Failed to set panel overdrive: %d\n", err); + return err; + } + + if (result > 1) { + pr_warn("Failed to set panel overdrive (result): 0x%x\n", result); + return -EIO; + } + + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "panel_od"); return count; } - static DEVICE_ATTR_RW(panel_od); /* Quirks *********************************************************************/ @@ -1816,6 +1866,18 @@ static int asus_fan_set_auto(struct asus_wmi *asus) return -ENXIO; } + /* + * Modern models like the G713 also have GPU fan control (this is not AGFN) + */ + if (asus->gpu_fan_type == FAN_TYPE_SPEC83) { + status = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_FAN_CTRL, + 0, &retval); + if (status) + return status; + + if (retval != 1) + return -EIO; + } return 0; } @@ -1853,7 +1915,7 @@ static ssize_t pwm1_show(struct device *dev, value = -1; } - return sprintf(buf, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t pwm1_store(struct device *dev, @@ -1913,7 +1975,7 @@ static ssize_t fan1_input_show(struct device *dev, return -ENXIO; } - return sprintf(buf, "%d\n", value < 0 ? -1 : value*100); + return sysfs_emit(buf, "%d\n", value < 0 ? -1 : value * 100); } static ssize_t pwm1_enable_show(struct device *dev, @@ -1931,7 +1993,7 @@ static ssize_t pwm1_enable_show(struct device *dev, * in practice on X532FL at least (the bit is always 0) and there's * also nothing in the DSDT to indicate that this behaviour exists. */ - return sprintf(buf, "%d\n", asus->fan_pwm_mode); + return sysfs_emit(buf, "%d\n", asus->fan_pwm_mode); } static ssize_t pwm1_enable_store(struct device *dev, @@ -1999,7 +2061,7 @@ static ssize_t fan1_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", ASUS_FAN_DESC); + return sysfs_emit(buf, "%s\n", ASUS_FAN_DESC); } static ssize_t asus_hwmon_temp1(struct device *dev, @@ -2018,11 +2080,86 @@ static ssize_t asus_hwmon_temp1(struct device *dev, deci_kelvin_to_millicelsius(value & 0xFFFF)); } +/* GPU fan on modern ROG laptops */ +static ssize_t fan2_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + int value; + int ret; + + ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_GPU_FAN_CTRL, &value); + if (ret < 0) + return ret; + + value &= 0xffff; + + return sysfs_emit(buf, "%d\n", value < 0 ? -1 : value * 100); +} + +static ssize_t fan2_label_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", ASUS_GPU_FAN_DESC); +} + +static ssize_t pwm2_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", asus->gpu_fan_pwm_mode); +} + +static ssize_t pwm2_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + int state; + int value; + int ret; + u32 retval; + + ret = kstrtouint(buf, 10, &state); + if (ret) + return ret; + + switch (state) { /* standard documented hwmon values */ + case ASUS_FAN_CTRL_FULLSPEED: + value = 1; + break; + case ASUS_FAN_CTRL_AUTO: + value = 0; + break; + default: + return -EINVAL; + } + + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_FAN_CTRL, + value, &retval); + if (ret) + return ret; + + if (retval != 1) + return -EIO; + + asus->gpu_fan_pwm_mode = state; + return count; +} + /* Fan1 */ static DEVICE_ATTR_RW(pwm1); static DEVICE_ATTR_RW(pwm1_enable); static DEVICE_ATTR_RO(fan1_input); static DEVICE_ATTR_RO(fan1_label); +/* Fan2 - GPU fan */ +static DEVICE_ATTR_RW(pwm2_enable); +static DEVICE_ATTR_RO(fan2_input); +static DEVICE_ATTR_RO(fan2_label); /* Temperature */ static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL); @@ -2030,8 +2167,11 @@ static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL); static struct attribute *hwmon_attributes[] = { &dev_attr_pwm1.attr, &dev_attr_pwm1_enable.attr, + &dev_attr_pwm2_enable.attr, &dev_attr_fan1_input.attr, &dev_attr_fan1_label.attr, + &dev_attr_fan2_input.attr, + &dev_attr_fan2_label.attr, &dev_attr_temp1_input.attr, NULL @@ -2040,7 +2180,7 @@ static struct attribute *hwmon_attributes[] = { static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct asus_wmi *asus = dev_get_drvdata(dev->parent); u32 value = ASUS_WMI_UNSUPPORTED_METHOD; @@ -2052,6 +2192,11 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, || attr == &dev_attr_pwm1_enable.attr) { if (asus->fan_type == FAN_TYPE_NONE) return 0; + } else if (attr == &dev_attr_fan2_input.attr + || attr == &dev_attr_fan2_label.attr + || attr == &dev_attr_pwm2_enable.attr) { + if (asus->gpu_fan_type == FAN_TYPE_NONE) + return 0; } else if (attr == &dev_attr_temp1_input.attr) { int err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, @@ -2094,6 +2239,7 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) static int asus_wmi_fan_init(struct asus_wmi *asus) { + asus->gpu_fan_type = FAN_TYPE_NONE; asus->fan_type = FAN_TYPE_NONE; asus->agfn_pwm = -1; @@ -2102,6 +2248,10 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) else if (asus_wmi_has_agfn_fan(asus)) asus->fan_type = FAN_TYPE_AGFN; + /* Modern models like G713 also have GPU fan control */ + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_FAN_CTRL)) + asus->gpu_fan_type = FAN_TYPE_SPEC83; + if (asus->fan_type == FAN_TYPE_NONE) return -ENODEV; @@ -2192,7 +2342,7 @@ static ssize_t fan_boost_mode_show(struct device *dev, { struct asus_wmi *asus = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_boost_mode); + return sysfs_emit(buf, "%d\n", asus->fan_boost_mode); } static ssize_t fan_boost_mode_store(struct device *dev, @@ -2744,7 +2894,7 @@ static ssize_t throttle_thermal_policy_show(struct device *dev, struct asus_wmi *asus = dev_get_drvdata(dev); u8 mode = asus->throttle_thermal_policy_mode; - return scnprintf(buf, PAGE_SIZE, "%d\n", mode); + return sysfs_emit(buf, "%d\n", mode); } static ssize_t throttle_thermal_policy_store(struct device *dev, @@ -3096,9 +3246,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) { unsigned int key_value = 1; bool autorelease = 1; - int result, orig_code; - - orig_code = code; + int orig_code = code; if (asus->driver->key_filter) { asus->driver->key_filter(asus->driver, &code, &key_value, @@ -3141,38 +3289,18 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } - if (asus->driver->quirks->tablet_switch_mode == asus_wmi_kbd_dock_devid && - code == NOTIFY_KBD_DOCK_CHANGE) { - result = asus_wmi_get_devstate_simple(asus, - ASUS_WMI_DEVID_KBD_DOCK); - if (result >= 0) { - input_report_switch(asus->inputdev, SW_TABLET_MODE, - !result); - input_sync(asus->inputdev); - } - return; - } - - if (asus->driver->quirks->tablet_switch_mode == asus_wmi_lid_flip_devid && - code == NOTIFY_LID_FLIP) { - lid_flip_tablet_mode_get_state(asus); - return; - } - - if (asus->driver->quirks->tablet_switch_mode == asus_wmi_lid_flip_rog_devid && - code == NOTIFY_LID_FLIP_ROG) { - lid_flip_rog_tablet_mode_get_state(asus); + if (code == asus->tablet_switch_event_code) { + asus_wmi_tablet_mode_get_state(asus); return; } - if (asus->fan_boost_mode_available && code == NOTIFY_KBD_FBM) { - fan_boost_mode_switch_next(asus); + if (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) { + if (asus->fan_boost_mode_available) + fan_boost_mode_switch_next(asus); + if (asus->throttle_thermal_policy_available) + throttle_thermal_policy_switch_next(asus); return; - } - if (asus->throttle_thermal_policy_available && code == NOTIFY_KBD_TTP) { - throttle_thermal_policy_switch_next(asus); - return; } if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle) @@ -3324,6 +3452,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_touchpad.attr, &dev_attr_egpu_enable.attr, &dev_attr_dgpu_disable.attr, + &dev_attr_gpu_mux_mode.attr, &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, @@ -3335,7 +3464,7 @@ static struct attribute *platform_attributes[] = { static umode_t asus_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct asus_wmi *asus = dev_get_drvdata(dev); bool ok = true; int devid = -1; @@ -3354,6 +3483,10 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, ok = asus->egpu_enable_available; else if (attr == &dev_attr_dgpu_disable.attr) ok = asus->dgpu_disable_available; + else if (attr == &dev_attr_dgpu_disable.attr) + ok = asus->dgpu_disable_available; + else if (attr == &dev_attr_gpu_mux_mode.attr) + ok = asus->gpu_mux_mode_available; else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; else if (attr == &dev_attr_throttle_thermal_policy.attr) @@ -3615,13 +3748,12 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform; - err = egpu_enable_check_present(asus); - if (err) - goto fail_egpu_enable; - - err = dgpu_disable_check_present(asus); - if (err) - goto fail_dgpu_disable; + asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU); + asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU); + asus->gpu_mux_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX); + asus->kbd_rgb_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE); + asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE); + asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD); err = fan_boost_mode_check_present(asus); if (err) @@ -3637,10 +3769,6 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform_profile_setup; - err = panel_od_check_present(asus); - if (err) - goto fail_panel_od; - err = asus_wmi_sysfs_init(asus->platform_device); if (err) goto fail_sysfs; @@ -3735,10 +3863,7 @@ static int asus_wmi_add(struct platform_device *pdev) if (asus->platform_profile_support) platform_profile_remove(); fail_fan_boost_mode: -fail_egpu_enable: -fail_dgpu_disable: fail_platform: -fail_panel_od: kfree(asus); return err; } @@ -3797,18 +3922,7 @@ static int asus_hotk_resume(struct device *device) if (asus_wmi_has_fnlock_key(asus)) asus_wmi_fnlock_update(asus); - switch (asus->driver->quirks->tablet_switch_mode) { - case asus_wmi_no_tablet_switch: - case asus_wmi_kbd_dock_devid: - break; - case asus_wmi_lid_flip_devid: - lid_flip_tablet_mode_get_state(asus); - break; - case asus_wmi_lid_flip_rog_devid: - lid_flip_rog_tablet_mode_get_state(asus); - break; - } - + asus_wmi_tablet_mode_get_state(asus); return 0; } @@ -3848,18 +3962,7 @@ static int asus_hotk_restore(struct device *device) if (asus_wmi_has_fnlock_key(asus)) asus_wmi_fnlock_update(asus); - switch (asus->driver->quirks->tablet_switch_mode) { - case asus_wmi_no_tablet_switch: - case asus_wmi_kbd_dock_devid: - break; - case asus_wmi_lid_flip_devid: - lid_flip_tablet_mode_get_state(asus); - break; - case asus_wmi_lid_flip_rog_devid: - lid_flip_rog_tablet_mode_get_state(asus); - break; - } - + asus_wmi_tablet_mode_get_state(asus); return 0; } diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 7c96db7f3060..28234dc9fa6a 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -79,6 +79,7 @@ #define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 #define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */ #define ASUS_WMI_DEVID_CPU_FAN_CTRL 0x00110013 +#define ASUS_WMI_DEVID_GPU_FAN_CTRL 0x00110014 #define ASUS_WMI_DEVID_CPU_FAN_CURVE 0x00110024 #define ASUS_WMI_DEVID_GPU_FAN_CURVE 0x00110025 @@ -100,6 +101,15 @@ /* dgpu on/off */ #define ASUS_WMI_DEVID_DGPU 0x00090020 +/* gpu mux switch, 0 = dGPU, 1 = Optimus */ +#define ASUS_WMI_DEVID_GPU_MUX 0x00090016 + +/* TUF laptop RGB modes/colours */ +#define ASUS_WMI_DEVID_TUF_RGB_MODE 0x00100056 + +/* TUF laptop RGB power/state */ +#define ASUS_WMI_DEVID_TUF_RGB_STATE 0x00100057 + /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 -- 2.38.1