generated from kernel-packages/linux-pikaos-6.12.10
7941 lines
252 KiB
Diff
7941 lines
252 KiB
Diff
From 2da2427f9b97edebaf226c6a35e6eddfb0c8650f Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Fri, 31 Jan 2025 09:14:52 +1300
|
|
Subject: [PATCH 03/28] platform/x86: asus-wmi: Remove all ROG Ally CSEE hacks
|
|
|
|
Remove the hacks introduced by these commits:
|
|
- fc73fe3b8501 "platform/x86: asus-wmi: disable USB0 hub on ROG Ally before suspend"
|
|
- 6222853365c5 "platform/x86: asus-wmi: ROG Ally increase wait time, allow MCU powersave"
|
|
- 5e9a9192205f "platform/x86: asus-wmi: Add quirk for ROG Ally X"
|
|
|
|
The suspend issue the above was trying to fix has been corrected in MCU
|
|
firmware updates by ASUS, where the root cause of issues was a flag in
|
|
the MCU powersave path that was not being cleared on resume, leading to
|
|
every second resume leaving the MCU in a powered off state.
|
|
|
|
The fixed MCU firmware has been available since 2024/10/09.
|
|
Minimum versions:
|
|
- ROG Ally 1: v319
|
|
- ROG Ally X: v313
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/platform/x86/asus-wmi.c | 40 ---------------------------------
|
|
1 file changed, 40 deletions(-)
|
|
|
|
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
|
index 8bd187e8b47f..01fc39d64f27 100644
|
|
--- a/drivers/platform/x86/asus-wmi.c
|
|
+++ b/drivers/platform/x86/asus-wmi.c
|
|
@@ -142,11 +142,6 @@ module_param(fnlock_default, bool, 0444);
|
|
#define ASUS_MINI_LED_2024_STRONG 0x01
|
|
#define ASUS_MINI_LED_2024_OFF 0x02
|
|
|
|
-/* Controls the power state of the USB0 hub on ROG Ally which input is on */
|
|
-#define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE"
|
|
-/* 300ms so far seems to produce a reliable result on AC and battery */
|
|
-#define ASUS_USB0_PWR_EC0_CSEE_WAIT 1500
|
|
-
|
|
static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
|
|
|
|
static int throttle_thermal_policy_write(struct asus_wmi *);
|
|
@@ -274,9 +269,6 @@ struct asus_wmi {
|
|
u32 tablet_switch_dev_id;
|
|
bool tablet_switch_inverted;
|
|
|
|
- /* The ROG Ally device requires the MCU USB device be disconnected before suspend */
|
|
- bool ally_mcu_usb_switch;
|
|
-
|
|
enum fan_type fan_type;
|
|
enum fan_type gpu_fan_type;
|
|
enum fan_type mid_fan_type;
|
|
@@ -4719,8 +4711,6 @@ static int asus_wmi_add(struct platform_device *pdev)
|
|
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->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE);
|
|
- asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE)
|
|
- && dmi_check_system(asus_ally_mcu_quirk);
|
|
|
|
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE))
|
|
asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
|
|
@@ -4911,34 +4901,6 @@ static int asus_hotk_resume(struct device *device)
|
|
return 0;
|
|
}
|
|
|
|
-static int asus_hotk_resume_early(struct device *device)
|
|
-{
|
|
- struct asus_wmi *asus = dev_get_drvdata(device);
|
|
-
|
|
- if (asus->ally_mcu_usb_switch) {
|
|
- /* sleep required to prevent USB0 being yanked then reappearing rapidly */
|
|
- if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB8)))
|
|
- dev_err(device, "ROG Ally MCU failed to connect USB dev\n");
|
|
- else
|
|
- msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT);
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int asus_hotk_prepare(struct device *device)
|
|
-{
|
|
- struct asus_wmi *asus = dev_get_drvdata(device);
|
|
-
|
|
- if (asus->ally_mcu_usb_switch) {
|
|
- /* sleep required to ensure USB0 is disabled before sleep continues */
|
|
- if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB7)))
|
|
- dev_err(device, "ROG Ally MCU failed to disconnect USB dev\n");
|
|
- else
|
|
- msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT);
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
static int asus_hotk_restore(struct device *device)
|
|
{
|
|
struct asus_wmi *asus = dev_get_drvdata(device);
|
|
@@ -4983,8 +4945,6 @@ static const struct dev_pm_ops asus_pm_ops = {
|
|
.thaw = asus_hotk_thaw,
|
|
.restore = asus_hotk_restore,
|
|
.resume = asus_hotk_resume,
|
|
- .resume_early = asus_hotk_resume_early,
|
|
- .prepare = asus_hotk_prepare,
|
|
};
|
|
|
|
/* Registration ***************************************************************/
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 228ab740c4377f7b9e11ae88e4429c3c060fe18b Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sun, 22 Sep 2024 21:40:46 +1200
|
|
Subject: [PATCH 04/28] platform/x86: asus-wmi: export symbols used for
|
|
read/write WMI
|
|
|
|
Export some rather helpful read/write WMI symbols using a namespace.
|
|
These are DEVS and DSTS only, or require the arg0 input.
|
|
|
|
Also does a slight refactor of internals of these functions.
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
|
|
---
|
|
drivers/hid/hid-asus.c | 1 +
|
|
drivers/platform/x86/asus-wmi.c | 44 ++++++++++++++++++++--
|
|
include/linux/platform_data/x86/asus-wmi.h | 10 +++++
|
|
3 files changed, 52 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
|
|
index 46e3e42f9eb5..2df6a1ca57ba 100644
|
|
--- a/drivers/hid/hid-asus.c
|
|
+++ b/drivers/hid/hid-asus.c
|
|
@@ -1327,4 +1327,5 @@ static struct hid_driver asus_driver = {
|
|
};
|
|
module_hid_driver(asus_driver);
|
|
|
|
+MODULE_IMPORT_NS("ASUS_WMI");
|
|
MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
|
index 01fc39d64f27..a0e0a7490688 100644
|
|
--- a/drivers/platform/x86/asus-wmi.c
|
|
+++ b/drivers/platform/x86/asus-wmi.c
|
|
@@ -377,7 +377,7 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
|
|
{
|
|
return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);
|
|
}
|
|
-EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
|
|
+EXPORT_SYMBOL_NS_GPL(asus_wmi_evaluate_method, "ASUS_WMI");
|
|
|
|
static int asus_wmi_evaluate_method5(u32 method_id,
|
|
u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 *retval)
|
|
@@ -541,12 +541,50 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
|
|
return 0;
|
|
}
|
|
|
|
-static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
|
|
- u32 *retval)
|
|
+/**
|
|
+ * asus_wmi_get_devstate_dsts() - Get the WMI function state.
|
|
+ * @dev_id: The WMI method ID to call.
|
|
+ * @retval: A pointer to where to store the value returned from WMI.
|
|
+ *
|
|
+ * On success the return value is 0, and the retval is a valid value returned
|
|
+ * by the successful WMI function call otherwise an error is returned if the
|
|
+ * call failed, or if the WMI method ID is unsupported.
|
|
+ */
|
|
+int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, retval);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (*retval == ASUS_WMI_UNSUPPORTED_METHOD)
|
|
+ return -ENODEV;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(asus_wmi_get_devstate_dsts, "ASUS_WMI");
|
|
+
|
|
+/**
|
|
+ * asus_wmi_set_devstate() - Set the WMI function state.
|
|
+ * @dev_id: The WMI function to call.
|
|
+ * @ctrl_param: The argument to be used for this WMI function.
|
|
+ * @retval: A pointer to where to store the value returned from WMI.
|
|
+ *
|
|
+ * The returned WMI function state if not checked here for error as
|
|
+ * asus_wmi_set_devstate() is not called unless first paired with a call to
|
|
+ * asus_wmi_get_devstate_dsts() to check that the WMI function is supported.
|
|
+ *
|
|
+ * On success the return value is 0, and the retval is a valid value returned
|
|
+ * by the successful WMI function call. An error value is returned only if the
|
|
+ * WMI function failed.
|
|
+ */
|
|
+int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval)
|
|
{
|
|
return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
|
|
ctrl_param, retval);
|
|
}
|
|
+EXPORT_SYMBOL_NS_GPL(asus_wmi_set_devstate, "ASUS_WMI");
|
|
|
|
/* Helper for special devices with magic return codes */
|
|
static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
|
|
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
|
index 783e2a336861..466781cbb4bb 100644
|
|
--- a/include/linux/platform_data/x86/asus-wmi.h
|
|
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
|
@@ -158,8 +158,18 @@
|
|
#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F
|
|
|
|
#if IS_REACHABLE(CONFIG_ASUS_WMI)
|
|
+int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval);
|
|
+int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
|
|
int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
|
|
#else
|
|
+static inline int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
|
|
+{
|
|
+ return -ENODEV;
|
|
+}
|
|
+static inline int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval)
|
|
+{
|
|
+ return -ENODEV;
|
|
+}
|
|
static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
|
|
u32 *retval)
|
|
{
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From a52d59fec1f510c974eab71221fe7b0088d3ac59 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Tue, 28 Jan 2025 19:29:27 +1300
|
|
Subject: [PATCH 05/28] hid-asus: check ROG Ally MCU version and warn
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus.c | 97 +++++++++++++++++++++++++++++++++++++++++-
|
|
1 file changed, 95 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
|
|
index 2df6a1ca57ba..adccc2346ec3 100644
|
|
--- a/drivers/hid/hid-asus.c
|
|
+++ b/drivers/hid/hid-asus.c
|
|
@@ -52,6 +52,10 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
|
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
|
#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
|
|
|
+#define ROG_ALLY_REPORT_SIZE 64
|
|
+#define ROG_ALLY_X_MIN_MCU 313
|
|
+#define ROG_ALLY_MIN_MCU 319
|
|
+
|
|
#define SUPPORT_KBD_BACKLIGHT BIT(0)
|
|
|
|
#define MAX_TOUCH_MAJOR 8
|
|
@@ -84,6 +88,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
|
#define QUIRK_MEDION_E1239T BIT(10)
|
|
#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)
|
|
#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
|
|
+#define QUIRK_ROG_ALLY_XPAD BIT(13)
|
|
|
|
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
|
|
QUIRK_NO_INIT_REPORTS | \
|
|
@@ -534,9 +539,89 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
|
|
return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
|
|
}
|
|
|
|
+/*
|
|
+ * We don't care about any other part of the string except the version section.
|
|
+ * Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01
|
|
+ */
|
|
+static int mcu_parse_version_string(const u8 *response, size_t response_size)
|
|
+{
|
|
+ int dot_count = 0;
|
|
+ size_t i;
|
|
+
|
|
+ // Look for the second '.' to identify the start of the version
|
|
+ for (i = 0; i < response_size; i++) {
|
|
+ if (response[i] == '.') {
|
|
+ dot_count++;
|
|
+ if (dot_count == 2) {
|
|
+ int version =
|
|
+ simple_strtol((const char *)&response[i + 1], NULL, 10);
|
|
+ return (version >= 0) ? version : -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int mcu_request_version(struct hid_device *hdev)
|
|
+{
|
|
+ const u8 request[] = { 0x5a, 0x05, 0x03, 0x31, 0x00, 0x20 };
|
|
+ u8 *response;
|
|
+ int ret;
|
|
+
|
|
+ response = kzalloc(ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!response)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = asus_kbd_set_report(hdev, request, sizeof(request));
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+
|
|
+ ret = hid_hw_raw_request(hdev, FEATURE_REPORT_ID, response,
|
|
+ ROG_ALLY_REPORT_SIZE, HID_FEATURE_REPORT,
|
|
+ HID_REQ_GET_REPORT);
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+
|
|
+ ret = mcu_parse_version_string(response, ROG_ALLY_REPORT_SIZE);
|
|
+out:
|
|
+ if (ret < 0)
|
|
+ hid_err(hdev, "Failed to get MCU version: %d\n", ret);
|
|
+ kfree(response);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void mcu_maybe_warn_version(struct hid_device *hdev, int idProduct)
|
|
+{
|
|
+ int min_version, version;
|
|
+
|
|
+ min_version = ROG_ALLY_X_MIN_MCU;
|
|
+ version = mcu_request_version(hdev);
|
|
+ if (version) {
|
|
+ switch (idProduct) {
|
|
+ case USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY:
|
|
+ min_version = ROG_ALLY_MIN_MCU;
|
|
+ break;
|
|
+ case USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X:
|
|
+ min_version = ROG_ALLY_X_MIN_MCU;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ hid_info(hdev, "Ally device MCU version: %d\n", version);
|
|
+ if (version < min_version) {
|
|
+ hid_warn(hdev,
|
|
+ "The MCU version must be %d or greater\n"
|
|
+ "Please update your MCU with official ASUS firmware release\n",
|
|
+ min_version);
|
|
+ }
|
|
+}
|
|
+
|
|
static int asus_kbd_register_leds(struct hid_device *hdev)
|
|
{
|
|
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
|
+ struct usb_interface *intf;
|
|
+ struct usb_device *udev;
|
|
unsigned char kbd_func;
|
|
int ret;
|
|
|
|
@@ -560,6 +645,14 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
+
|
|
+ if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
|
|
+ intf = to_usb_interface(hdev->dev.parent);
|
|
+ udev = interface_to_usbdev(intf);
|
|
+ mcu_maybe_warn_version(hdev,
|
|
+ le16_to_cpu(udev->descriptor.idProduct));
|
|
+ }
|
|
+
|
|
} else {
|
|
/* Initialize keyboard */
|
|
ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
|
|
@@ -1280,10 +1373,10 @@ static const struct hid_device_id asus_devices[] = {
|
|
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
|
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY),
|
|
- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
|
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_ALLY_XPAD},
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
|
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X),
|
|
- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
|
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_ALLY_XPAD },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
|
USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD),
|
|
QUIRK_ROG_CLAYMORE_II_KEYBOARD },
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From bdf1c20de6fb8fd2b61e64430db00732cb43d3f0 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Thu, 30 Jan 2025 20:55:02 +1300
|
|
Subject: [PATCH 06/28] asus-wmi: disable mcu_powersave if MCU version too low
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus.c | 12 ++
|
|
drivers/platform/x86/asus-wmi.c | 132 +-------------------
|
|
include/linux/platform_data/x86/asus-wmi.h | 138 +++++++++++++++++++++
|
|
3 files changed, 152 insertions(+), 130 deletions(-)
|
|
|
|
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
|
|
index adccc2346ec3..641bf1f8c747 100644
|
|
--- a/drivers/hid/hid-asus.c
|
|
+++ b/drivers/hid/hid-asus.c
|
|
@@ -27,6 +27,7 @@
|
|
#include <linux/hid.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_data/x86/asus-wmi.h>
|
|
+#include <linux/platform_device.h>
|
|
#include <linux/input/mt.h>
|
|
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
|
|
#include <linux/power_supply.h>
|
|
@@ -594,6 +595,8 @@ static int mcu_request_version(struct hid_device *hdev)
|
|
static void mcu_maybe_warn_version(struct hid_device *hdev, int idProduct)
|
|
{
|
|
int min_version, version;
|
|
+ struct asus_wmi *asus;
|
|
+ struct device *dev;
|
|
|
|
min_version = ROG_ALLY_X_MIN_MCU;
|
|
version = mcu_request_version(hdev);
|
|
@@ -614,6 +617,15 @@ static void mcu_maybe_warn_version(struct hid_device *hdev, int idProduct)
|
|
"The MCU version must be %d or greater\n"
|
|
"Please update your MCU with official ASUS firmware release\n",
|
|
min_version);
|
|
+ /* Get the asus platform device */
|
|
+ dev = bus_find_device_by_name(&platform_bus_type, NULL, "asus-nb-wmi");
|
|
+ if (dev) {
|
|
+ asus = dev_get_drvdata(dev);
|
|
+ /* Do not show the powersave attribute if MCU version too low */
|
|
+ if (asus)
|
|
+ asus->mcu_powersave_available = false;
|
|
+ put_device(dev);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
|
index a0e0a7490688..c02d9fa89799 100644
|
|
--- a/drivers/platform/x86/asus-wmi.c
|
|
+++ b/drivers/platform/x86/asus-wmi.c
|
|
@@ -198,135 +198,6 @@ struct agfn_fan_args {
|
|
u32 speed; /* read: RPM/100 - write: 0-255 */
|
|
} __packed;
|
|
|
|
-/*
|
|
- * <platform>/ - debugfs root directory
|
|
- * dev_id - current dev_id
|
|
- * ctrl_param - current ctrl_param
|
|
- * method_id - current method_id
|
|
- * devs - call DEVS(dev_id, ctrl_param) and print result
|
|
- * dsts - call DSTS(dev_id) and print result
|
|
- * call - call method_id(dev_id, ctrl_param) and print result
|
|
- */
|
|
-struct asus_wmi_debug {
|
|
- struct dentry *root;
|
|
- u32 method_id;
|
|
- u32 dev_id;
|
|
- u32 ctrl_param;
|
|
-};
|
|
-
|
|
-struct asus_rfkill {
|
|
- struct asus_wmi *asus;
|
|
- struct rfkill *rfkill;
|
|
- u32 dev_id;
|
|
-};
|
|
-
|
|
-enum fan_type {
|
|
- FAN_TYPE_NONE = 0,
|
|
- FAN_TYPE_AGFN, /* deprecated on newer platforms */
|
|
- FAN_TYPE_SPEC83, /* starting in Spec 8.3, use CPU_FAN_CTRL */
|
|
-};
|
|
-
|
|
-struct fan_curve_data {
|
|
- bool enabled;
|
|
- u32 device_id;
|
|
- u8 temps[FAN_CURVE_POINTS];
|
|
- u8 percents[FAN_CURVE_POINTS];
|
|
-};
|
|
-
|
|
-struct asus_wmi {
|
|
- int dsts_id;
|
|
- int spec;
|
|
- int sfun;
|
|
-
|
|
- struct input_dev *inputdev;
|
|
- struct backlight_device *backlight_device;
|
|
- struct backlight_device *screenpad_backlight_device;
|
|
- struct platform_device *platform_device;
|
|
-
|
|
- struct led_classdev wlan_led;
|
|
- int wlan_led_wk;
|
|
- struct led_classdev tpd_led;
|
|
- int tpd_led_wk;
|
|
- struct led_classdev kbd_led;
|
|
- int kbd_led_wk;
|
|
- struct led_classdev lightbar_led;
|
|
- int lightbar_led_wk;
|
|
- struct led_classdev micmute_led;
|
|
- struct led_classdev camera_led;
|
|
- struct workqueue_struct *led_workqueue;
|
|
- struct work_struct tpd_led_work;
|
|
- struct work_struct wlan_led_work;
|
|
- struct work_struct lightbar_led_work;
|
|
-
|
|
- struct asus_rfkill wlan;
|
|
- struct asus_rfkill bluetooth;
|
|
- struct asus_rfkill wimax;
|
|
- struct asus_rfkill wwan3g;
|
|
- struct asus_rfkill gps;
|
|
- struct asus_rfkill uwb;
|
|
-
|
|
- int tablet_switch_event_code;
|
|
- u32 tablet_switch_dev_id;
|
|
- bool tablet_switch_inverted;
|
|
-
|
|
- enum fan_type fan_type;
|
|
- enum fan_type gpu_fan_type;
|
|
- enum fan_type mid_fan_type;
|
|
- int fan_pwm_mode;
|
|
- int gpu_fan_pwm_mode;
|
|
- int mid_fan_pwm_mode;
|
|
- int agfn_pwm;
|
|
-
|
|
- bool fan_boost_mode_available;
|
|
- u8 fan_boost_mode_mask;
|
|
- u8 fan_boost_mode;
|
|
-
|
|
- bool egpu_enable_available;
|
|
- bool dgpu_disable_available;
|
|
- u32 gpu_mux_dev;
|
|
-
|
|
- /* Tunables provided by ASUS for gaming laptops */
|
|
- u32 ppt_pl2_sppt;
|
|
- u32 ppt_pl1_spl;
|
|
- u32 ppt_apu_sppt;
|
|
- u32 ppt_platform_sppt;
|
|
- u32 ppt_fppt;
|
|
- u32 nv_dynamic_boost;
|
|
- u32 nv_temp_target;
|
|
-
|
|
- u32 kbd_rgb_dev;
|
|
- bool kbd_rgb_state_available;
|
|
-
|
|
- u8 throttle_thermal_policy_mode;
|
|
- u32 throttle_thermal_policy_dev;
|
|
-
|
|
- bool cpu_fan_curve_available;
|
|
- bool gpu_fan_curve_available;
|
|
- bool mid_fan_curve_available;
|
|
- struct fan_curve_data custom_fan_curves[3];
|
|
-
|
|
- struct platform_profile_handler platform_profile_handler;
|
|
- bool platform_profile_support;
|
|
-
|
|
- // The RSOC controls the maximum charging percentage.
|
|
- bool battery_rsoc_available;
|
|
-
|
|
- bool panel_overdrive_available;
|
|
- u32 mini_led_dev_id;
|
|
-
|
|
- struct hotplug_slot hotplug_slot;
|
|
- struct mutex hotplug_lock;
|
|
- struct mutex wmi_lock;
|
|
- struct workqueue_struct *hotplug_workqueue;
|
|
- struct work_struct hotplug_work;
|
|
-
|
|
- bool fnlock_locked;
|
|
-
|
|
- struct asus_wmi_debug debug;
|
|
-
|
|
- struct asus_wmi_driver *driver;
|
|
-};
|
|
-
|
|
/* WMI ************************************************************************/
|
|
|
|
static int asus_wmi_evaluate_method3(u32 method_id,
|
|
@@ -4489,7 +4360,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
|
else if (attr == &dev_attr_nv_temp_target.attr)
|
|
devid = ASUS_WMI_DEVID_NV_THERM_TARGET;
|
|
else if (attr == &dev_attr_mcu_powersave.attr)
|
|
- devid = ASUS_WMI_DEVID_MCU_POWERSAVE;
|
|
+ ok = asus->mcu_powersave_available;
|
|
else if (attr == &dev_attr_boot_sound.attr)
|
|
devid = ASUS_WMI_DEVID_BOOT_SOUND;
|
|
else if (attr == &dev_attr_panel_od.attr)
|
|
@@ -4749,6 +4620,7 @@ static int asus_wmi_add(struct platform_device *pdev)
|
|
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->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE);
|
|
+ asus->mcu_powersave_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MCU_POWERSAVE);
|
|
|
|
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE))
|
|
asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
|
|
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
|
index 466781cbb4bb..4e6de0c97a78 100644
|
|
--- a/include/linux/platform_data/x86/asus-wmi.h
|
|
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
|
@@ -5,6 +5,144 @@
|
|
#include <linux/errno.h>
|
|
#include <linux/types.h>
|
|
#include <linux/dmi.h>
|
|
+#include <linux/leds.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/pci_hotplug.h>
|
|
+#include <linux/platform_profile.h>
|
|
+
|
|
+/*
|
|
+ * <platform>/ - debugfs root directory
|
|
+ * dev_id - current dev_id
|
|
+ * ctrl_param - current ctrl_param
|
|
+ * method_id - current method_id
|
|
+ * devs - call DEVS(dev_id, ctrl_param) and print result
|
|
+ * dsts - call DSTS(dev_id) and print result
|
|
+ * call - call method_id(dev_id, ctrl_param) and print result
|
|
+ */
|
|
+struct asus_wmi_debug {
|
|
+ struct dentry *root;
|
|
+ u32 method_id;
|
|
+ u32 dev_id;
|
|
+ u32 ctrl_param;
|
|
+};
|
|
+
|
|
+struct asus_rfkill {
|
|
+ struct asus_wmi *asus;
|
|
+ struct rfkill *rfkill;
|
|
+ u32 dev_id;
|
|
+};
|
|
+
|
|
+enum fan_type {
|
|
+ FAN_TYPE_NONE = 0,
|
|
+ FAN_TYPE_AGFN, /* deprecated on newer platforms */
|
|
+ FAN_TYPE_SPEC83, /* starting in Spec 8.3, use CPU_FAN_CTRL */
|
|
+};
|
|
+
|
|
+#define FAN_CURVE_POINTS 8
|
|
+
|
|
+struct fan_curve_data {
|
|
+ bool enabled;
|
|
+ u32 device_id;
|
|
+ u8 temps[FAN_CURVE_POINTS];
|
|
+ u8 percents[FAN_CURVE_POINTS];
|
|
+};
|
|
+
|
|
+struct asus_wmi {
|
|
+ int dsts_id;
|
|
+ int spec;
|
|
+ int sfun;
|
|
+
|
|
+ struct input_dev *inputdev;
|
|
+ struct backlight_device *backlight_device;
|
|
+ struct backlight_device *screenpad_backlight_device;
|
|
+ struct platform_device *platform_device;
|
|
+
|
|
+ struct led_classdev wlan_led;
|
|
+ int wlan_led_wk;
|
|
+ struct led_classdev tpd_led;
|
|
+ int tpd_led_wk;
|
|
+ struct led_classdev kbd_led;
|
|
+ int kbd_led_wk;
|
|
+ struct led_classdev lightbar_led;
|
|
+ int lightbar_led_wk;
|
|
+ struct led_classdev micmute_led;
|
|
+ struct led_classdev camera_led;
|
|
+ struct workqueue_struct *led_workqueue;
|
|
+ struct work_struct tpd_led_work;
|
|
+ struct work_struct wlan_led_work;
|
|
+ struct work_struct lightbar_led_work;
|
|
+
|
|
+ struct asus_rfkill wlan;
|
|
+ struct asus_rfkill bluetooth;
|
|
+ struct asus_rfkill wimax;
|
|
+ struct asus_rfkill wwan3g;
|
|
+ struct asus_rfkill gps;
|
|
+ struct asus_rfkill uwb;
|
|
+
|
|
+ int tablet_switch_event_code;
|
|
+ u32 tablet_switch_dev_id;
|
|
+ bool tablet_switch_inverted;
|
|
+
|
|
+ bool mcu_powersave_available;
|
|
+
|
|
+ enum fan_type fan_type;
|
|
+ enum fan_type gpu_fan_type;
|
|
+ enum fan_type mid_fan_type;
|
|
+ int fan_pwm_mode;
|
|
+ int gpu_fan_pwm_mode;
|
|
+ int mid_fan_pwm_mode;
|
|
+ int agfn_pwm;
|
|
+
|
|
+ bool fan_boost_mode_available;
|
|
+ u8 fan_boost_mode_mask;
|
|
+ u8 fan_boost_mode;
|
|
+
|
|
+ bool egpu_enable_available;
|
|
+ bool dgpu_disable_available;
|
|
+ u32 gpu_mux_dev;
|
|
+
|
|
+ /* Tunables provided by ASUS for gaming laptops */
|
|
+ u32 ppt_pl2_sppt;
|
|
+ u32 ppt_pl1_spl;
|
|
+ u32 ppt_apu_sppt;
|
|
+ u32 ppt_platform_sppt;
|
|
+ u32 ppt_fppt;
|
|
+ u32 nv_dynamic_boost;
|
|
+ u32 nv_temp_target;
|
|
+
|
|
+ u32 kbd_rgb_dev;
|
|
+ bool kbd_rgb_state_available;
|
|
+
|
|
+ u8 throttle_thermal_policy_mode;
|
|
+ u32 throttle_thermal_policy_dev;
|
|
+
|
|
+ bool cpu_fan_curve_available;
|
|
+ bool gpu_fan_curve_available;
|
|
+ bool mid_fan_curve_available;
|
|
+ struct fan_curve_data custom_fan_curves[3];
|
|
+
|
|
+ // struct device *ppdev;
|
|
+ struct platform_profile_handler platform_profile_handler;
|
|
+ bool platform_profile_support;
|
|
+
|
|
+ // The RSOC controls the maximum charging percentage.
|
|
+ bool battery_rsoc_available;
|
|
+
|
|
+ bool panel_overdrive_available;
|
|
+ u32 mini_led_dev_id;
|
|
+
|
|
+ struct hotplug_slot hotplug_slot;
|
|
+ struct mutex hotplug_lock;
|
|
+ struct mutex wmi_lock;
|
|
+ struct workqueue_struct *hotplug_workqueue;
|
|
+ struct work_struct hotplug_work;
|
|
+
|
|
+ bool fnlock_locked;
|
|
+
|
|
+ struct asus_wmi_debug debug;
|
|
+
|
|
+ struct asus_wmi_driver *driver;
|
|
+};
|
|
|
|
/* WMI Methods */
|
|
#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 9480632d43f796b298bfb2fdbea5e4bb9ca2a340 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Mon, 26 Aug 2024 12:49:35 +1200
|
|
Subject: [PATCH 07/28] hid-asus-ally: Add joystick LED ring support
|
|
|
|
Adds basic support for the joystick RGB LED rings as a multicolour LED
|
|
device with 4 LEDs.
|
|
|
|
Move the MCU check in to new driver as it must be done after init, and
|
|
hid-asus-ally takes over from hid-asus.
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/Kconfig | 9 +
|
|
drivers/hid/Makefile | 1 +
|
|
drivers/hid/hid-asus-ally.c | 627 ++++++++++++++++++++++++++++++++++++
|
|
drivers/hid/hid-asus-ally.h | 38 +++
|
|
drivers/hid/hid-asus.c | 116 +------
|
|
5 files changed, 689 insertions(+), 102 deletions(-)
|
|
create mode 100644 drivers/hid/hid-asus-ally.c
|
|
create mode 100644 drivers/hid/hid-asus-ally.h
|
|
|
|
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
|
|
index 4d2a89d65b65..89357822c27b 100644
|
|
--- a/drivers/hid/Kconfig
|
|
+++ b/drivers/hid/Kconfig
|
|
@@ -164,6 +164,15 @@ config HID_ASUS
|
|
- GL553V series
|
|
- GL753V series
|
|
|
|
+config HID_ASUS_ALLY
|
|
+ tristate "Asus Ally gamepad configuration support"
|
|
+ depends on USB_HID
|
|
+ depends on LEDS_CLASS
|
|
+ depends on LEDS_CLASS_MULTICOLOR
|
|
+ select POWER_SUPPLY
|
|
+ help
|
|
+ Support for configuring the Asus ROG Ally gamepad using attributes.
|
|
+
|
|
config HID_AUREAL
|
|
tristate "Aureal"
|
|
help
|
|
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
|
|
index 24de45f3677d..f338c9eb4600 100644
|
|
--- a/drivers/hid/Makefile
|
|
+++ b/drivers/hid/Makefile
|
|
@@ -31,6 +31,7 @@ obj-$(CONFIG_HID_APPLE) += hid-apple.o
|
|
obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o
|
|
obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o
|
|
obj-$(CONFIG_HID_ASUS) += hid-asus.o
|
|
+obj-$(CONFIG_HID_ASUS_ALLY) += hid-asus-ally.o
|
|
obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
|
|
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
|
|
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
new file mode 100644
|
|
index 000000000000..4ceb4aab8084
|
|
--- /dev/null
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -0,0 +1,627 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+/*
|
|
+ * HID driver for Asus ROG laptops and Ally
|
|
+ *
|
|
+ * Copyright (c) 2023 Luke Jones <luke@ljones.dev>
|
|
+ */
|
|
+
|
|
+#include "linux/device.h"
|
|
+#include <linux/platform_data/x86/asus-wmi.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include "linux/pm.h"
|
|
+#include "linux/slab.h"
|
|
+#include "linux/stddef.h"
|
|
+#include <linux/hid.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/leds.h>
|
|
+#include <linux/led-class-multicolor.h>
|
|
+
|
|
+#include "hid-ids.h"
|
|
+#include "hid-asus-ally.h"
|
|
+
|
|
+#define READY_MAX_TRIES 3
|
|
+#define FEATURE_REPORT_ID 0x0d
|
|
+#define FEATURE_ROG_ALLY_REPORT_ID 0x5a
|
|
+#define FEATURE_ROG_ALLY_CODE_PAGE 0xD1
|
|
+#define FEATURE_ROG_ALLY_REPORT_SIZE 64
|
|
+#define ALLY_X_INPUT_REPORT_USB 0x0B
|
|
+#define ALLY_X_INPUT_REPORT_USB_SIZE 16
|
|
+
|
|
+#define ROG_ALLY_REPORT_SIZE 64
|
|
+#define ROG_ALLY_X_MIN_MCU 313
|
|
+#define ROG_ALLY_MIN_MCU 319
|
|
+
|
|
+#define ROG_ALLY_CFG_INTF_IN 0x83
|
|
+#define ROG_ALLY_CFG_INTF_OUT 0x04
|
|
+#define ROG_ALLY_X_INTF_IN 0x87
|
|
+
|
|
+#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
|
+#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
|
+
|
|
+static const u8 EC_INIT_STRING[] = { 0x5A, 'A', 'S', 'U', 'S', ' ', 'T', 'e','c', 'h', '.', 'I', 'n', 'c', '.', '\0' };
|
|
+static const u8 EC_MODE_LED_APPLY[] = { 0x5A, 0xB4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
+static const u8 EC_MODE_LED_SET[] = { 0x5A, 0xB5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
+static const u8 FORCE_FEEDBACK_OFF[] = { 0x0D, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xEB };
|
|
+
|
|
+static const struct hid_device_id rog_ally_devices[] = {
|
|
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY) },
|
|
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X) },
|
|
+ {}
|
|
+};
|
|
+
|
|
+struct ally_rgb_dev {
|
|
+ struct hid_device *hdev;
|
|
+ struct led_classdev_mc led_rgb_dev;
|
|
+ struct work_struct work;
|
|
+ bool output_worker_initialized;
|
|
+ spinlock_t lock;
|
|
+
|
|
+ bool removed;
|
|
+ bool update_rgb;
|
|
+ uint8_t red[4];
|
|
+ uint8_t green[4];
|
|
+ uint8_t blue[4];
|
|
+};
|
|
+
|
|
+struct ally_rgb_data {
|
|
+ uint8_t brightness;
|
|
+ uint8_t red[4];
|
|
+ uint8_t green[4];
|
|
+ uint8_t blue[4];
|
|
+ bool initialized;
|
|
+};
|
|
+
|
|
+static struct ally_drvdata {
|
|
+ struct hid_device *hdev;
|
|
+ struct ally_rgb_dev *led_rgb_dev;
|
|
+ struct ally_rgb_data led_rgb_data;
|
|
+} drvdata;
|
|
+
|
|
+/**
|
|
+ * asus_dev_set_report - send set report request to device.
|
|
+ *
|
|
+ * @hdev: hid device
|
|
+ * @buf: in/out data to transfer
|
|
+ * @len: length of buf
|
|
+ *
|
|
+ * Return: count of data transferred, negative if error
|
|
+ *
|
|
+ * Same behavior as hid_hw_raw_request. Note that the input buffer is duplicated.
|
|
+ */
|
|
+static int asus_dev_set_report(struct hid_device *hdev, const u8 *buf, size_t len)
|
|
+{
|
|
+ unsigned char *dmabuf;
|
|
+ int ret;
|
|
+
|
|
+ dmabuf = kmemdup(buf, len, GFP_KERNEL);
|
|
+ if (!dmabuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = hid_hw_raw_request(hdev, buf[0], dmabuf, len, HID_FEATURE_REPORT,
|
|
+ HID_REQ_SET_REPORT);
|
|
+ kfree(dmabuf);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int asus_dev_get_report(struct hid_device *hdev, u8 *out_buf, size_t out_buf_size)
|
|
+{
|
|
+ return hid_hw_raw_request(hdev, FEATURE_REPORT_ID, out_buf, out_buf_size,
|
|
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
|
+}
|
|
+
|
|
+static u8 get_endpoint_address(struct hid_device *hdev)
|
|
+{
|
|
+ struct usb_interface *intf;
|
|
+ struct usb_host_endpoint *ep;
|
|
+
|
|
+ intf = to_usb_interface(hdev->dev.parent);
|
|
+
|
|
+ if (intf) {
|
|
+ ep = intf->cur_altsetting->endpoint;
|
|
+ if (ep) {
|
|
+ return ep->desc.bEndpointAddress;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+/**************************************************************************************************/
|
|
+/* ROG Ally LED control */
|
|
+/**************************************************************************************************/
|
|
+static void ally_rgb_schedule_work(struct ally_rgb_dev *led)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&led->lock, flags);
|
|
+ if (!led->removed)
|
|
+ schedule_work(&led->work);
|
|
+ spin_unlock_irqrestore(&led->lock, flags);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * The RGB still has the basic 0-3 level brightness. Since the multicolour
|
|
+ * brightness is being used in place, set this to max
|
|
+ */
|
|
+static int ally_rgb_set_bright_base_max(struct hid_device *hdev)
|
|
+{
|
|
+ u8 buf[] = { FEATURE_KBD_LED_REPORT_ID1, 0xba, 0xc5, 0xc4, 0x02 };
|
|
+
|
|
+ return asus_dev_set_report(hdev, buf, sizeof(buf));
|
|
+}
|
|
+
|
|
+static void ally_rgb_do_work(struct work_struct *work)
|
|
+{
|
|
+ struct ally_rgb_dev *led = container_of(work, struct ally_rgb_dev, work);
|
|
+ int ret;
|
|
+ unsigned long flags;
|
|
+
|
|
+ u8 buf[16] = { [0] = FEATURE_ROG_ALLY_REPORT_ID,
|
|
+ [1] = FEATURE_ROG_ALLY_CODE_PAGE,
|
|
+ [2] = xpad_cmd_set_leds,
|
|
+ [3] = xpad_cmd_len_leds };
|
|
+
|
|
+ spin_lock_irqsave(&led->lock, flags);
|
|
+ if (!led->update_rgb) {
|
|
+ spin_unlock_irqrestore(&led->lock, flags);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (int i = 0; i < 4; i++) {
|
|
+ buf[5 + i * 3] = drvdata.led_rgb_dev->green[i];
|
|
+ buf[6 + i * 3] = drvdata.led_rgb_dev->blue[i];
|
|
+ buf[4 + i * 3] = drvdata.led_rgb_dev->red[i];
|
|
+ }
|
|
+ led->update_rgb = false;
|
|
+
|
|
+ spin_unlock_irqrestore(&led->lock, flags);
|
|
+
|
|
+ ret = asus_dev_set_report(led->hdev, buf, sizeof(buf));
|
|
+ if (ret < 0)
|
|
+ hid_err(led->hdev, "Ally failed to set gamepad backlight: %d\n", ret);
|
|
+}
|
|
+
|
|
+static void ally_rgb_set(struct led_classdev *cdev, enum led_brightness brightness)
|
|
+{
|
|
+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
|
|
+ struct ally_rgb_dev *led = container_of(mc_cdev, struct ally_rgb_dev, led_rgb_dev);
|
|
+ int intensity, bright;
|
|
+ unsigned long flags;
|
|
+
|
|
+ led_mc_calc_color_components(mc_cdev, brightness);
|
|
+ spin_lock_irqsave(&led->lock, flags);
|
|
+ led->update_rgb = true;
|
|
+ bright = mc_cdev->led_cdev.brightness;
|
|
+ for (int i = 0; i < 4; i++) {
|
|
+ intensity = mc_cdev->subled_info[i].intensity;
|
|
+ drvdata.led_rgb_dev->red[i] = (((intensity >> 16) & 0xFF) * bright) / 255;
|
|
+ drvdata.led_rgb_dev->green[i] = (((intensity >> 8) & 0xFF) * bright) / 255;
|
|
+ drvdata.led_rgb_dev->blue[i] = ((intensity & 0xFF) * bright) / 255;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&led->lock, flags);
|
|
+ drvdata.led_rgb_data.initialized = true;
|
|
+
|
|
+ ally_rgb_schedule_work(led);
|
|
+}
|
|
+
|
|
+static int ally_rgb_set_static_from_multi(struct hid_device *hdev)
|
|
+{
|
|
+ u8 buf[17] = {FEATURE_KBD_LED_REPORT_ID1, 0xb3};
|
|
+ int ret;
|
|
+
|
|
+ /*
|
|
+ * Set single zone single colour based on the first LED of EC software mode.
|
|
+ * buf[2] = zone, buf[3] = mode
|
|
+ */
|
|
+ buf[4] = drvdata.led_rgb_data.red[0];
|
|
+ buf[5] = drvdata.led_rgb_data.green[0];
|
|
+ buf[6] = drvdata.led_rgb_data.blue[0];
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, buf, sizeof(buf));
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, EC_MODE_LED_APPLY, sizeof(EC_MODE_LED_APPLY));
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return asus_dev_set_report(hdev, EC_MODE_LED_SET, sizeof(EC_MODE_LED_SET));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Store the RGB values for restoring on resume, and set the static mode to the first LED colour
|
|
+*/
|
|
+static void ally_rgb_store_settings(void)
|
|
+{
|
|
+ int arr_size = sizeof(drvdata.led_rgb_data.red);
|
|
+
|
|
+ struct ally_rgb_dev *led_rgb = drvdata.led_rgb_dev;
|
|
+
|
|
+ drvdata.led_rgb_data.brightness = led_rgb->led_rgb_dev.led_cdev.brightness;
|
|
+
|
|
+ memcpy(drvdata.led_rgb_data.red, led_rgb->red, arr_size);
|
|
+ memcpy(drvdata.led_rgb_data.green, led_rgb->green, arr_size);
|
|
+ memcpy(drvdata.led_rgb_data.blue, led_rgb->blue, arr_size);
|
|
+
|
|
+ ally_rgb_set_static_from_multi(led_rgb->hdev);
|
|
+}
|
|
+
|
|
+static void ally_rgb_restore_settings(struct ally_rgb_dev *led_rgb, struct led_classdev *led_cdev,
|
|
+ struct mc_subled *mc_led_info)
|
|
+{
|
|
+ int arr_size = sizeof(drvdata.led_rgb_data.red);
|
|
+
|
|
+ memcpy(led_rgb->red, drvdata.led_rgb_data.red, arr_size);
|
|
+ memcpy(led_rgb->green, drvdata.led_rgb_data.green, arr_size);
|
|
+ memcpy(led_rgb->blue, drvdata.led_rgb_data.blue, arr_size);
|
|
+ for (int i = 0; i < 4; i++) {
|
|
+ mc_led_info[i].intensity = (drvdata.led_rgb_data.red[i] << 16) |
|
|
+ (drvdata.led_rgb_data.green[i] << 8) |
|
|
+ drvdata.led_rgb_data.blue[i];
|
|
+ }
|
|
+ led_cdev->brightness = drvdata.led_rgb_data.brightness;
|
|
+}
|
|
+
|
|
+/* Set LEDs. Call after any setup. */
|
|
+static void ally_rgb_resume(void)
|
|
+{
|
|
+ struct ally_rgb_dev *led_rgb = drvdata.led_rgb_dev;
|
|
+ struct led_classdev *led_cdev;
|
|
+ struct mc_subled *mc_led_info;
|
|
+
|
|
+ if (!led_rgb)
|
|
+ return;
|
|
+
|
|
+ led_cdev = &led_rgb->led_rgb_dev.led_cdev;
|
|
+ mc_led_info = led_rgb->led_rgb_dev.subled_info;
|
|
+
|
|
+ if (drvdata.led_rgb_data.initialized) {
|
|
+ ally_rgb_restore_settings(led_rgb, led_cdev, mc_led_info);
|
|
+ led_rgb->update_rgb = true;
|
|
+ ally_rgb_schedule_work(led_rgb);
|
|
+ ally_rgb_set_bright_base_max(led_rgb->hdev);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ally_rgb_register(struct hid_device *hdev, struct ally_rgb_dev *led_rgb)
|
|
+{
|
|
+ struct mc_subled *mc_led_info;
|
|
+ struct led_classdev *led_cdev;
|
|
+
|
|
+ mc_led_info =
|
|
+ devm_kmalloc_array(&hdev->dev, 12, sizeof(*mc_led_info), GFP_KERNEL | __GFP_ZERO);
|
|
+ if (!mc_led_info)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mc_led_info[0].color_index = LED_COLOR_ID_RGB;
|
|
+ mc_led_info[1].color_index = LED_COLOR_ID_RGB;
|
|
+ mc_led_info[2].color_index = LED_COLOR_ID_RGB;
|
|
+ mc_led_info[3].color_index = LED_COLOR_ID_RGB;
|
|
+
|
|
+ led_rgb->led_rgb_dev.subled_info = mc_led_info;
|
|
+ led_rgb->led_rgb_dev.num_colors = 4;
|
|
+
|
|
+ led_cdev = &led_rgb->led_rgb_dev.led_cdev;
|
|
+ led_cdev->brightness = 128;
|
|
+ led_cdev->name = "ally:rgb:joystick_rings";
|
|
+ led_cdev->max_brightness = 255;
|
|
+ led_cdev->brightness_set = ally_rgb_set;
|
|
+
|
|
+ if (drvdata.led_rgb_data.initialized) {
|
|
+ ally_rgb_restore_settings(led_rgb, led_cdev, mc_led_info);
|
|
+ }
|
|
+
|
|
+ return devm_led_classdev_multicolor_register(&hdev->dev, &led_rgb->led_rgb_dev);
|
|
+}
|
|
+
|
|
+static struct ally_rgb_dev *ally_rgb_create(struct hid_device *hdev)
|
|
+{
|
|
+ struct ally_rgb_dev *led_rgb;
|
|
+ int ret;
|
|
+
|
|
+ led_rgb = devm_kzalloc(&hdev->dev, sizeof(struct ally_rgb_dev), GFP_KERNEL);
|
|
+ if (!led_rgb)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ ret = ally_rgb_register(hdev, led_rgb);
|
|
+ if (ret < 0) {
|
|
+ cancel_work_sync(&led_rgb->work);
|
|
+ devm_kfree(&hdev->dev, led_rgb);
|
|
+ return ERR_PTR(ret);
|
|
+ }
|
|
+
|
|
+ led_rgb->hdev = hdev;
|
|
+ led_rgb->removed = false;
|
|
+
|
|
+ INIT_WORK(&led_rgb->work, ally_rgb_do_work);
|
|
+ led_rgb->output_worker_initialized = true;
|
|
+ spin_lock_init(&led_rgb->lock);
|
|
+
|
|
+ ally_rgb_set_bright_base_max(hdev);
|
|
+
|
|
+ /* Not marked as initialized unless ally_rgb_set() is called */
|
|
+ if (drvdata.led_rgb_data.initialized) {
|
|
+ msleep(1500);
|
|
+ led_rgb->update_rgb = true;
|
|
+ ally_rgb_schedule_work(led_rgb);
|
|
+ }
|
|
+
|
|
+ return led_rgb;
|
|
+}
|
|
+
|
|
+static void ally_rgb_remove(struct hid_device *hdev)
|
|
+{
|
|
+ struct ally_rgb_dev *led_rgb = drvdata.led_rgb_dev;
|
|
+ unsigned long flags;
|
|
+ int ep;
|
|
+
|
|
+ ep = get_endpoint_address(hdev);
|
|
+ if (ep != ROG_ALLY_CFG_INTF_IN)
|
|
+ return;
|
|
+
|
|
+ if (!drvdata.led_rgb_dev || led_rgb->removed)
|
|
+ return;
|
|
+
|
|
+ spin_lock_irqsave(&led_rgb->lock, flags);
|
|
+ led_rgb->removed = true;
|
|
+ led_rgb->output_worker_initialized = false;
|
|
+ spin_unlock_irqrestore(&led_rgb->lock, flags);
|
|
+ cancel_work_sync(&led_rgb->work);
|
|
+ devm_led_classdev_multicolor_unregister(&hdev->dev, &led_rgb->led_rgb_dev);
|
|
+
|
|
+ hid_info(hdev, "Removed Ally RGB interface");
|
|
+}
|
|
+
|
|
+/**************************************************************************************************/
|
|
+/* ROG Ally driver init */
|
|
+/**************************************************************************************************/
|
|
+
|
|
+/*
|
|
+ * We don't care about any other part of the string except the version section.
|
|
+ * Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01
|
|
+ */
|
|
+static int mcu_parse_version_string(const u8 *response, size_t response_size)
|
|
+{
|
|
+ int dot_count = 0;
|
|
+ size_t i;
|
|
+
|
|
+ // Look for the second '.' to identify the start of the version
|
|
+ for (i = 0; i < response_size; i++) {
|
|
+ if (response[i] == '.') {
|
|
+ dot_count++;
|
|
+ if (dot_count == 2) {
|
|
+ int version =
|
|
+ simple_strtol((const char *)&response[i + 1], NULL, 10);
|
|
+ return (version >= 0) ? version : -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int mcu_request_version(struct hid_device *hdev)
|
|
+{
|
|
+ const u8 request[] = { 0x5a, 0x05, 0x03, 0x31, 0x00, 0x20 };
|
|
+ u8 *response;
|
|
+ int ret;
|
|
+
|
|
+ response = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!response)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, request, sizeof(request));
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+
|
|
+ ret = asus_dev_get_report(hdev, response, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+
|
|
+ ret = mcu_parse_version_string(response, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+out:
|
|
+ if (ret < 0)
|
|
+ hid_err(hdev, "Failed to get MCU version: %d\n", ret);
|
|
+ kfree(response);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void mcu_maybe_warn_version(struct hid_device *hdev, int idProduct)
|
|
+{
|
|
+ int min_version, version;
|
|
+ struct asus_wmi *asus;
|
|
+ struct device *dev;
|
|
+
|
|
+ min_version = ROG_ALLY_X_MIN_MCU;
|
|
+ version = mcu_request_version(hdev);
|
|
+ if (version) {
|
|
+ switch (idProduct) {
|
|
+ case USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY:
|
|
+ min_version = ROG_ALLY_MIN_MCU;
|
|
+ break;
|
|
+ case USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X:
|
|
+ min_version = ROG_ALLY_X_MIN_MCU;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ hid_info(hdev, "Ally device MCU version: %d\n", version);
|
|
+ if (version < min_version) {
|
|
+ hid_warn(hdev,
|
|
+ "The MCU version must be %d or greater\n"
|
|
+ "Please update your MCU with official ASUS firmware release\n",
|
|
+ min_version);
|
|
+ /* Get the asus platform device */
|
|
+ dev = bus_find_device_by_name(&platform_bus_type, NULL, "asus-nb-wmi");
|
|
+ if (dev) {
|
|
+ asus = dev_get_drvdata(dev);
|
|
+ /* Do not show the powersave attribute if MCU version too low */
|
|
+ if (asus)
|
|
+ asus->mcu_powersave_available = false;
|
|
+ put_device(dev);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static int ally_hid_init(struct hid_device *hdev)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, EC_INIT_STRING, sizeof(EC_INIT_STRING));
|
|
+ if (ret < 0) {
|
|
+ hid_err(hdev, "Ally failed to send init command: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, FORCE_FEEDBACK_OFF, sizeof(FORCE_FEEDBACK_OFF));
|
|
+ if (ret < 0)
|
|
+ hid_err(hdev, "Ally failed to send init command: %d\n", ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ally_hid_probe(struct hid_device *hdev, const struct hid_device_id *_id)
|
|
+{
|
|
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
|
+ struct usb_device *udev = interface_to_usbdev(intf);
|
|
+ u16 idProduct = le16_to_cpu(udev->descriptor.idProduct);
|
|
+ int ret, ep;
|
|
+
|
|
+ ep = get_endpoint_address(hdev);
|
|
+ if (ep < 0)
|
|
+ return ep;
|
|
+
|
|
+ if (ep != ROG_ALLY_CFG_INTF_IN)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = hid_parse(hdev);
|
|
+ if (ret) {
|
|
+ hid_err(hdev, "Parse failed\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
|
+ if (ret) {
|
|
+ hid_err(hdev, "Failed to start HID device\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = hid_hw_open(hdev);
|
|
+ if (ret) {
|
|
+ hid_err(hdev, "Failed to open HID device\n");
|
|
+ goto err_stop;
|
|
+ }
|
|
+
|
|
+ /* Initialize MCU even before alloc */
|
|
+ ret = ally_hid_init(hdev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ drvdata.hdev = hdev;
|
|
+ hid_set_drvdata(hdev, &drvdata);
|
|
+
|
|
+ /* This should almost always exist */
|
|
+ if (ep == ROG_ALLY_CFG_INTF_IN) {
|
|
+ mcu_maybe_warn_version(hdev, idProduct);
|
|
+
|
|
+ drvdata.led_rgb_dev = ally_rgb_create(hdev);
|
|
+ if (IS_ERR(drvdata.led_rgb_dev))
|
|
+ hid_err(hdev, "Failed to create Ally gamepad LEDs.\n");
|
|
+ else
|
|
+ hid_info(hdev, "Created Ally RGB LED controls.\n");
|
|
+
|
|
+ if (IS_ERR(drvdata.led_rgb_dev))
|
|
+ goto err_close;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_close:
|
|
+ hid_hw_close(hdev);
|
|
+err_stop:
|
|
+ hid_hw_stop(hdev);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void ally_hid_remove(struct hid_device *hdev)
|
|
+{
|
|
+ if (drvdata.led_rgb_dev)
|
|
+ ally_rgb_remove(hdev);
|
|
+
|
|
+ hid_hw_close(hdev);
|
|
+ hid_hw_stop(hdev);
|
|
+}
|
|
+
|
|
+static int ally_hid_resume(struct hid_device *hdev)
|
|
+{
|
|
+ ally_rgb_resume();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ally_hid_reset_resume(struct hid_device *hdev)
|
|
+{
|
|
+ int ep = get_endpoint_address(hdev);
|
|
+ if (ep != ROG_ALLY_CFG_INTF_IN)
|
|
+ return 0;
|
|
+
|
|
+ ally_hid_init(hdev);
|
|
+ ally_rgb_resume();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ally_pm_thaw(struct device *dev)
|
|
+{
|
|
+ struct hid_device *hdev = to_hid_device(dev);
|
|
+
|
|
+ return ally_hid_reset_resume(hdev);
|
|
+}
|
|
+
|
|
+static int ally_pm_suspend(struct device *dev)
|
|
+{
|
|
+ if (drvdata.led_rgb_dev) {
|
|
+ ally_rgb_store_settings();
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops ally_pm_ops = {
|
|
+ .thaw = ally_pm_thaw,
|
|
+ .suspend = ally_pm_suspend,
|
|
+ .poweroff = ally_pm_suspend,
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(hid, rog_ally_devices);
|
|
+
|
|
+static struct hid_driver
|
|
+ rog_ally_cfg = { .name = "asus_rog_ally",
|
|
+ .id_table = rog_ally_devices,
|
|
+ .probe = ally_hid_probe,
|
|
+ .remove = ally_hid_remove,
|
|
+ /* HID is the better place for resume functions, not pm_ops */
|
|
+ .resume = ally_hid_resume,
|
|
+ .reset_resume = ally_hid_reset_resume,
|
|
+ .driver = {
|
|
+ .pm = &ally_pm_ops,
|
|
+ } };
|
|
+
|
|
+static int __init rog_ally_init(void)
|
|
+{
|
|
+ return hid_register_driver(&rog_ally_cfg);
|
|
+}
|
|
+
|
|
+static void __exit rog_ally_exit(void)
|
|
+{
|
|
+ hid_unregister_driver(&rog_ally_cfg);
|
|
+}
|
|
+
|
|
+module_init(rog_ally_init);
|
|
+module_exit(rog_ally_exit);
|
|
+
|
|
+MODULE_AUTHOR("Luke D. Jones");
|
|
+MODULE_DESCRIPTION("HID Driver for ASUS ROG Ally gamepad configuration.");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
new file mode 100644
|
|
index 000000000000..eb8617c80c2a
|
|
--- /dev/null
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -0,0 +1,38 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
+ *
|
|
+ * HID driver for Asus ROG laptops and Ally
|
|
+ *
|
|
+ * Copyright (c) 2023 Luke Jones <luke@ljones.dev>
|
|
+ */
|
|
+
|
|
+#include <linux/hid.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+/* the xpad_cmd determines which feature is set or queried */
|
|
+enum xpad_cmd {
|
|
+ xpad_cmd_set_mode = 0x01,
|
|
+ xpad_cmd_set_mapping = 0x02,
|
|
+ xpad_cmd_set_js_dz = 0x04, /* deadzones */
|
|
+ xpad_cmd_set_tr_dz = 0x05, /* deadzones */
|
|
+ xpad_cmd_set_vibe_intensity = 0x06,
|
|
+ xpad_cmd_set_leds = 0x08,
|
|
+ xpad_cmd_check_ready = 0x0A,
|
|
+ xpad_cmd_set_calibration = 0x0D,
|
|
+ xpad_cmd_set_turbo = 0x0F,
|
|
+ xpad_cmd_set_response_curve = 0x13,
|
|
+ xpad_cmd_set_adz = 0x18,
|
|
+};
|
|
+
|
|
+/* the xpad_cmd determines which feature is set or queried */
|
|
+enum xpad_cmd_len {
|
|
+ xpad_cmd_len_mode = 0x01,
|
|
+ xpad_cmd_len_mapping = 0x2c,
|
|
+ xpad_cmd_len_deadzone = 0x04,
|
|
+ xpad_cmd_len_vibe_intensity = 0x02,
|
|
+ xpad_cmd_len_leds = 0x0C,
|
|
+ xpad_cmd_len_calibration2 = 0x01,
|
|
+ xpad_cmd_len_calibration3 = 0x01,
|
|
+ xpad_cmd_len_turbo = 0x20,
|
|
+ xpad_cmd_len_response_curve = 0x09,
|
|
+ xpad_cmd_len_adz = 0x02,
|
|
+};
|
|
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
|
|
index 641bf1f8c747..bd28250777f1 100644
|
|
--- a/drivers/hid/hid-asus.c
|
|
+++ b/drivers/hid/hid-asus.c
|
|
@@ -53,9 +53,9 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
|
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
|
#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
|
|
|
-#define ROG_ALLY_REPORT_SIZE 64
|
|
-#define ROG_ALLY_X_MIN_MCU 313
|
|
-#define ROG_ALLY_MIN_MCU 319
|
|
+#define ROG_ALLY_CFG_INTF_IN 0x83
|
|
+#define ROG_ALLY_CFG_INTF_OUT 0x04
|
|
+#define ROG_ALLY_X_INTF_IN 0x87
|
|
|
|
#define SUPPORT_KBD_BACKLIGHT BIT(0)
|
|
|
|
@@ -540,100 +540,9 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
|
|
return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
|
|
}
|
|
|
|
-/*
|
|
- * We don't care about any other part of the string except the version section.
|
|
- * Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01
|
|
- */
|
|
-static int mcu_parse_version_string(const u8 *response, size_t response_size)
|
|
-{
|
|
- int dot_count = 0;
|
|
- size_t i;
|
|
-
|
|
- // Look for the second '.' to identify the start of the version
|
|
- for (i = 0; i < response_size; i++) {
|
|
- if (response[i] == '.') {
|
|
- dot_count++;
|
|
- if (dot_count == 2) {
|
|
- int version =
|
|
- simple_strtol((const char *)&response[i + 1], NULL, 10);
|
|
- return (version >= 0) ? version : -EINVAL;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- return -EINVAL;
|
|
-}
|
|
-
|
|
-static int mcu_request_version(struct hid_device *hdev)
|
|
-{
|
|
- const u8 request[] = { 0x5a, 0x05, 0x03, 0x31, 0x00, 0x20 };
|
|
- u8 *response;
|
|
- int ret;
|
|
-
|
|
- response = kzalloc(ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
- if (!response)
|
|
- return -ENOMEM;
|
|
-
|
|
- ret = asus_kbd_set_report(hdev, request, sizeof(request));
|
|
- if (ret < 0)
|
|
- goto out;
|
|
-
|
|
- ret = hid_hw_raw_request(hdev, FEATURE_REPORT_ID, response,
|
|
- ROG_ALLY_REPORT_SIZE, HID_FEATURE_REPORT,
|
|
- HID_REQ_GET_REPORT);
|
|
- if (ret < 0)
|
|
- goto out;
|
|
-
|
|
- ret = mcu_parse_version_string(response, ROG_ALLY_REPORT_SIZE);
|
|
-out:
|
|
- if (ret < 0)
|
|
- hid_err(hdev, "Failed to get MCU version: %d\n", ret);
|
|
- kfree(response);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void mcu_maybe_warn_version(struct hid_device *hdev, int idProduct)
|
|
-{
|
|
- int min_version, version;
|
|
- struct asus_wmi *asus;
|
|
- struct device *dev;
|
|
-
|
|
- min_version = ROG_ALLY_X_MIN_MCU;
|
|
- version = mcu_request_version(hdev);
|
|
- if (version) {
|
|
- switch (idProduct) {
|
|
- case USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY:
|
|
- min_version = ROG_ALLY_MIN_MCU;
|
|
- break;
|
|
- case USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X:
|
|
- min_version = ROG_ALLY_X_MIN_MCU;
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- hid_info(hdev, "Ally device MCU version: %d\n", version);
|
|
- if (version < min_version) {
|
|
- hid_warn(hdev,
|
|
- "The MCU version must be %d or greater\n"
|
|
- "Please update your MCU with official ASUS firmware release\n",
|
|
- min_version);
|
|
- /* Get the asus platform device */
|
|
- dev = bus_find_device_by_name(&platform_bus_type, NULL, "asus-nb-wmi");
|
|
- if (dev) {
|
|
- asus = dev_get_drvdata(dev);
|
|
- /* Do not show the powersave attribute if MCU version too low */
|
|
- if (asus)
|
|
- asus->mcu_powersave_available = false;
|
|
- put_device(dev);
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
static int asus_kbd_register_leds(struct hid_device *hdev)
|
|
{
|
|
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
|
- struct usb_interface *intf;
|
|
- struct usb_device *udev;
|
|
unsigned char kbd_func;
|
|
int ret;
|
|
|
|
@@ -657,14 +566,6 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
-
|
|
- if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
|
|
- intf = to_usb_interface(hdev->dev.parent);
|
|
- udev = interface_to_usbdev(intf);
|
|
- mcu_maybe_warn_version(hdev,
|
|
- le16_to_cpu(udev->descriptor.idProduct));
|
|
- }
|
|
-
|
|
} else {
|
|
/* Initialize keyboard */
|
|
ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
|
|
@@ -1134,6 +1035,17 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
|
|
drvdata->quirks = id->driver_data;
|
|
|
|
+ /* Ignore these endpoints as they are used by hid-asus-ally */
|
|
+ if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
|
|
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
|
+ struct usb_host_endpoint *ep = intf->cur_altsetting->endpoint;
|
|
+
|
|
+ if (ep->desc.bEndpointAddress == ROG_ALLY_X_INTF_IN ||
|
|
+ ep->desc.bEndpointAddress == ROG_ALLY_CFG_INTF_IN ||
|
|
+ ep->desc.bEndpointAddress == ROG_ALLY_CFG_INTF_OUT)
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
/*
|
|
* T90CHI's keyboard dock returns same ID values as T100CHI's dock.
|
|
* Thus, identify T90CHI dock with product name string.
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 7d7558f4323d8f9187fbb082f7bb3ad4286a78ec Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Wed, 2 Oct 2024 23:32:46 +1300
|
|
Subject: [PATCH 08/28] hid-asus-ally: initial Ally-X gamepad
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 385 +++++++++++++++++++++++++++++++++++-
|
|
drivers/hid/hid-asus-ally.h | 5 +
|
|
2 files changed, 389 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 4ceb4aab8084..3d950535c3e2 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -50,6 +50,51 @@ static const struct hid_device_id rog_ally_devices[] = {
|
|
{}
|
|
};
|
|
|
|
+/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
+static const int hat_values[][2] = {
|
|
+ { 0, 0 }, { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 },
|
|
+ { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 },
|
|
+};
|
|
+
|
|
+/* rumble packet structure */
|
|
+struct ff_data {
|
|
+ u8 enable;
|
|
+ u8 magnitude_left;
|
|
+ u8 magnitude_right;
|
|
+ u8 magnitude_strong;
|
|
+ u8 magnitude_weak;
|
|
+ u8 pulse_sustain_10ms;
|
|
+ u8 pulse_release_10ms;
|
|
+ u8 loop_count;
|
|
+} __packed;
|
|
+
|
|
+struct ff_report {
|
|
+ u8 report_id;
|
|
+ struct ff_data ff;
|
|
+} __packed;
|
|
+
|
|
+struct ally_x_input_report {
|
|
+ uint16_t x, y;
|
|
+ uint16_t rx, ry;
|
|
+ uint16_t z, rz;
|
|
+ uint8_t buttons[4];
|
|
+} __packed;
|
|
+
|
|
+struct ally_x_device {
|
|
+ struct input_dev *input;
|
|
+ struct hid_device *hdev;
|
|
+ spinlock_t lock;
|
|
+
|
|
+ struct ff_report *ff_packet;
|
|
+ struct work_struct output_worker;
|
|
+ bool output_worker_initialized;
|
|
+ /* Prevent multiple queued event due to the enforced delay in worker */
|
|
+ bool update_qam_btn;
|
|
+ /* Set if the QAM and AC buttons emit Xbox and Xbox+A */
|
|
+ bool qam_btns_steam_mode;
|
|
+ bool update_ff;
|
|
+};
|
|
+
|
|
struct ally_rgb_dev {
|
|
struct hid_device *hdev;
|
|
struct led_classdev_mc led_rgb_dev;
|
|
@@ -74,6 +119,7 @@ struct ally_rgb_data {
|
|
|
|
static struct ally_drvdata {
|
|
struct hid_device *hdev;
|
|
+ struct ally_x_device *ally_x;
|
|
struct ally_rgb_dev *led_rgb_dev;
|
|
struct ally_rgb_data led_rgb_data;
|
|
} drvdata;
|
|
@@ -128,6 +174,309 @@ static u8 get_endpoint_address(struct hid_device *hdev)
|
|
return -ENODEV;
|
|
}
|
|
|
|
+/**************************************************************************************************/
|
|
+/* ROG Ally gamepad i/o and force-feedback */
|
|
+/**************************************************************************************************/
|
|
+static int ally_x_raw_event(struct ally_x_device *ally_x, struct hid_report *report, u8 *data,
|
|
+ int size)
|
|
+{
|
|
+ struct ally_x_input_report *in_report;
|
|
+ unsigned long flags;
|
|
+ u8 byte;
|
|
+
|
|
+ if (data[0] == 0x0B) {
|
|
+ in_report = (struct ally_x_input_report *)&data[1];
|
|
+
|
|
+ input_report_abs(ally_x->input, ABS_X, in_report->x);
|
|
+ input_report_abs(ally_x->input, ABS_Y, in_report->y);
|
|
+ input_report_abs(ally_x->input, ABS_RX, in_report->rx);
|
|
+ input_report_abs(ally_x->input, ABS_RY, in_report->ry);
|
|
+ input_report_abs(ally_x->input, ABS_Z, in_report->z);
|
|
+ input_report_abs(ally_x->input, ABS_RZ, in_report->rz);
|
|
+
|
|
+ byte = in_report->buttons[0];
|
|
+ input_report_key(ally_x->input, BTN_A, byte & BIT(0));
|
|
+ input_report_key(ally_x->input, BTN_B, byte & BIT(1));
|
|
+ input_report_key(ally_x->input, BTN_X, byte & BIT(2));
|
|
+ input_report_key(ally_x->input, BTN_Y, byte & BIT(3));
|
|
+ input_report_key(ally_x->input, BTN_TL, byte & BIT(4));
|
|
+ input_report_key(ally_x->input, BTN_TR, byte & BIT(5));
|
|
+ input_report_key(ally_x->input, BTN_SELECT, byte & BIT(6));
|
|
+ input_report_key(ally_x->input, BTN_START, byte & BIT(7));
|
|
+
|
|
+ byte = in_report->buttons[1];
|
|
+ input_report_key(ally_x->input, BTN_THUMBL, byte & BIT(0));
|
|
+ input_report_key(ally_x->input, BTN_THUMBR, byte & BIT(1));
|
|
+ input_report_key(ally_x->input, BTN_MODE, byte & BIT(2));
|
|
+
|
|
+ byte = in_report->buttons[2];
|
|
+ input_report_abs(ally_x->input, ABS_HAT0X, hat_values[byte][0]);
|
|
+ input_report_abs(ally_x->input, ABS_HAT0Y, hat_values[byte][1]);
|
|
+ }
|
|
+ /*
|
|
+ * The MCU used on Ally provides many devices: gamepad, keyboord, mouse, other.
|
|
+ * The AC and QAM buttons route through another interface making it difficult to
|
|
+ * use the events unless we grab those and use them here. Only works for Ally X.
|
|
+ */
|
|
+ else if (data[0] == 0x5A) {
|
|
+ if (ally_x->qam_btns_steam_mode) {
|
|
+ spin_lock_irqsave(&ally_x->lock, flags);
|
|
+ if (data[1] == 0x38 && !ally_x->update_qam_btn) {
|
|
+ ally_x->update_qam_btn = true;
|
|
+ if (ally_x->output_worker_initialized)
|
|
+ schedule_work(&ally_x->output_worker);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&ally_x->lock, flags);
|
|
+ /* Left/XBox button. Long press does ctrl+alt+del which we can't catch */
|
|
+ input_report_key(ally_x->input, BTN_MODE, data[1] == 0xA6);
|
|
+ } else {
|
|
+ input_report_key(ally_x->input, KEY_F16, data[1] == 0xA6);
|
|
+ input_report_key(ally_x->input, KEY_PROG1, data[1] == 0x38);
|
|
+ }
|
|
+ /* QAM long press */
|
|
+ input_report_key(ally_x->input, KEY_F17, data[1] == 0xA7);
|
|
+ /* QAM long press released */
|
|
+ input_report_key(ally_x->input, KEY_F18, data[1] == 0xA8);
|
|
+ }
|
|
+
|
|
+ input_sync(ally_x->input);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct input_dev *ally_x_alloc_input_dev(struct hid_device *hdev,
|
|
+ const char *name_suffix)
|
|
+{
|
|
+ struct input_dev *input_dev;
|
|
+
|
|
+ input_dev = devm_input_allocate_device(&hdev->dev);
|
|
+ if (!input_dev)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ input_dev->id.bustype = hdev->bus;
|
|
+ input_dev->id.vendor = hdev->vendor;
|
|
+ input_dev->id.product = hdev->product;
|
|
+ input_dev->id.version = hdev->version;
|
|
+ input_dev->uniq = hdev->uniq;
|
|
+ input_dev->name = "ASUS ROG Ally X Gamepad";
|
|
+
|
|
+ input_set_drvdata(input_dev, hdev);
|
|
+
|
|
+ return input_dev;
|
|
+}
|
|
+
|
|
+static int ally_x_play_effect(struct input_dev *idev, void *data, struct ff_effect *effect)
|
|
+{
|
|
+ struct ally_x_device *ally_x = drvdata.ally_x;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (effect->type != FF_RUMBLE)
|
|
+ return 0;
|
|
+
|
|
+ spin_lock_irqsave(&ally_x->lock, flags);
|
|
+ ally_x->ff_packet->ff.magnitude_strong = effect->u.rumble.strong_magnitude / 512;
|
|
+ ally_x->ff_packet->ff.magnitude_weak = effect->u.rumble.weak_magnitude / 512;
|
|
+ ally_x->update_ff = true;
|
|
+ spin_unlock_irqrestore(&ally_x->lock, flags);
|
|
+
|
|
+ if (ally_x->output_worker_initialized)
|
|
+ schedule_work(&ally_x->output_worker);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ally_x_work(struct work_struct *work)
|
|
+{
|
|
+ struct ally_x_device *ally_x = container_of(work, struct ally_x_device, output_worker);
|
|
+ struct ff_report *ff_report = NULL;
|
|
+ bool update_qam = false;
|
|
+ bool update_ff = false;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&ally_x->lock, flags);
|
|
+ update_ff = ally_x->update_ff;
|
|
+ if (ally_x->update_ff) {
|
|
+ ff_report = kmemdup(ally_x->ff_packet, sizeof(*ally_x->ff_packet), GFP_KERNEL);
|
|
+ ally_x->update_ff = false;
|
|
+ }
|
|
+ update_qam = ally_x->update_qam_btn;
|
|
+ spin_unlock_irqrestore(&ally_x->lock, flags);
|
|
+
|
|
+ if (update_ff && ff_report) {
|
|
+ ff_report->ff.magnitude_left = ff_report->ff.magnitude_strong;
|
|
+ ff_report->ff.magnitude_right = ff_report->ff.magnitude_weak;
|
|
+ asus_dev_set_report(ally_x->hdev, (u8 *)ff_report, sizeof(*ff_report));
|
|
+ }
|
|
+ kfree(ff_report);
|
|
+
|
|
+ if (update_qam) {
|
|
+ /*
|
|
+ * The sleeps here are required to allow steam to register the button combo.
|
|
+ */
|
|
+ usleep_range(1000, 2000);
|
|
+ input_report_key(ally_x->input, BTN_MODE, 1);
|
|
+ input_sync(ally_x->input);
|
|
+
|
|
+ msleep(80);
|
|
+ input_report_key(ally_x->input, BTN_A, 1);
|
|
+ input_sync(ally_x->input);
|
|
+
|
|
+ msleep(80);
|
|
+ input_report_key(ally_x->input, BTN_A, 0);
|
|
+ input_sync(ally_x->input);
|
|
+
|
|
+ msleep(80);
|
|
+ input_report_key(ally_x->input, BTN_MODE, 0);
|
|
+ input_sync(ally_x->input);
|
|
+
|
|
+ spin_lock_irqsave(&ally_x->lock, flags);
|
|
+ ally_x->update_qam_btn = false;
|
|
+ spin_unlock_irqrestore(&ally_x->lock, flags);
|
|
+ }
|
|
+}
|
|
+
|
|
+static struct input_dev *ally_x_setup_input(struct hid_device *hdev)
|
|
+{
|
|
+ int ret, abs_min = 0, js_abs_max = 65535, tr_abs_max = 1023;
|
|
+ struct input_dev *input;
|
|
+
|
|
+ input = ally_x_alloc_input_dev(hdev, NULL);
|
|
+ if (IS_ERR(input))
|
|
+ return ERR_CAST(input);
|
|
+
|
|
+ input_set_abs_params(input, ABS_X, abs_min, js_abs_max, 0, 0);
|
|
+ input_set_abs_params(input, ABS_Y, abs_min, js_abs_max, 0, 0);
|
|
+ input_set_abs_params(input, ABS_RX, abs_min, js_abs_max, 0, 0);
|
|
+ input_set_abs_params(input, ABS_RY, abs_min, js_abs_max, 0, 0);
|
|
+ input_set_abs_params(input, ABS_Z, abs_min, tr_abs_max, 0, 0);
|
|
+ input_set_abs_params(input, ABS_RZ, abs_min, tr_abs_max, 0, 0);
|
|
+ input_set_abs_params(input, ABS_HAT0X, -1, 1, 0, 0);
|
|
+ input_set_abs_params(input, ABS_HAT0Y, -1, 1, 0, 0);
|
|
+ input_set_capability(input, EV_KEY, BTN_A);
|
|
+ input_set_capability(input, EV_KEY, BTN_B);
|
|
+ input_set_capability(input, EV_KEY, BTN_X);
|
|
+ input_set_capability(input, EV_KEY, BTN_Y);
|
|
+ input_set_capability(input, EV_KEY, BTN_TL);
|
|
+ input_set_capability(input, EV_KEY, BTN_TR);
|
|
+ input_set_capability(input, EV_KEY, BTN_SELECT);
|
|
+ input_set_capability(input, EV_KEY, BTN_START);
|
|
+ input_set_capability(input, EV_KEY, BTN_MODE);
|
|
+ input_set_capability(input, EV_KEY, BTN_THUMBL);
|
|
+ input_set_capability(input, EV_KEY, BTN_THUMBR);
|
|
+
|
|
+ input_set_capability(input, EV_KEY, KEY_PROG1);
|
|
+ input_set_capability(input, EV_KEY, KEY_F16);
|
|
+ input_set_capability(input, EV_KEY, KEY_F17);
|
|
+ input_set_capability(input, EV_KEY, KEY_F18);
|
|
+
|
|
+ input_set_capability(input, EV_FF, FF_RUMBLE);
|
|
+ input_ff_create_memless(input, NULL, ally_x_play_effect);
|
|
+
|
|
+ ret = input_register_device(input);
|
|
+ if (ret)
|
|
+ return ERR_PTR(ret);
|
|
+
|
|
+ return input;
|
|
+}
|
|
+
|
|
+static ssize_t ally_x_qam_mode_show(struct device *dev, struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ struct ally_x_device *ally_x = drvdata.ally_x;
|
|
+
|
|
+ return sysfs_emit(buf, "%d\n", ally_x->qam_btns_steam_mode);
|
|
+}
|
|
+
|
|
+static ssize_t ally_x_qam_mode_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct ally_x_device *ally_x = drvdata.ally_x;
|
|
+ bool val;
|
|
+ int ret;
|
|
+
|
|
+ ret = kstrtobool(buf, &val);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ ally_x->qam_btns_steam_mode = val;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+ALLY_DEVICE_ATTR_RW(ally_x_qam_mode, qam_mode);
|
|
+
|
|
+static struct ally_x_device *ally_x_create(struct hid_device *hdev)
|
|
+{
|
|
+ uint8_t max_output_report_size;
|
|
+ struct ally_x_device *ally_x;
|
|
+ struct ff_report *report;
|
|
+ int ret;
|
|
+
|
|
+ ally_x = devm_kzalloc(&hdev->dev, sizeof(*ally_x), GFP_KERNEL);
|
|
+ if (!ally_x)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ ally_x->hdev = hdev;
|
|
+ INIT_WORK(&ally_x->output_worker, ally_x_work);
|
|
+ spin_lock_init(&ally_x->lock);
|
|
+ ally_x->output_worker_initialized = true;
|
|
+ ally_x->qam_btns_steam_mode =
|
|
+ true; /* Always default to steam mode, it can be changed by userspace attr */
|
|
+
|
|
+ max_output_report_size = sizeof(struct ally_x_input_report);
|
|
+ report = devm_kzalloc(&hdev->dev, sizeof(*report), GFP_KERNEL);
|
|
+ if (!report) {
|
|
+ ret = -ENOMEM;
|
|
+ goto free_ally_x;
|
|
+ }
|
|
+
|
|
+ /* None of these bytes will change for the FF command for now */
|
|
+ report->report_id = 0x0D;
|
|
+ report->ff.enable = 0x0F; /* Enable all by default */
|
|
+ report->ff.pulse_sustain_10ms = 0xFF; /* Duration */
|
|
+ report->ff.pulse_release_10ms = 0x00; /* Start Delay */
|
|
+ report->ff.loop_count = 0xEB; /* Loop Count */
|
|
+ ally_x->ff_packet = report;
|
|
+
|
|
+ ally_x->input = ally_x_setup_input(hdev);
|
|
+ if (IS_ERR(ally_x->input)) {
|
|
+ ret = PTR_ERR(ally_x->input);
|
|
+ goto free_ff_packet;
|
|
+ }
|
|
+
|
|
+ if (sysfs_create_file(&hdev->dev.kobj, &dev_attr_ally_x_qam_mode.attr)) {
|
|
+ ret = -ENODEV;
|
|
+ goto unregister_input;
|
|
+ }
|
|
+
|
|
+ ally_x->update_ff = true;
|
|
+ if (ally_x->output_worker_initialized)
|
|
+ schedule_work(&ally_x->output_worker);
|
|
+
|
|
+ hid_info(hdev, "Registered Ally X controller using %s\n",
|
|
+ dev_name(&ally_x->input->dev));
|
|
+ return ally_x;
|
|
+
|
|
+unregister_input:
|
|
+ input_unregister_device(ally_x->input);
|
|
+free_ff_packet:
|
|
+ kfree(ally_x->ff_packet);
|
|
+free_ally_x:
|
|
+ kfree(ally_x);
|
|
+ return ERR_PTR(ret);
|
|
+}
|
|
+
|
|
+static void ally_x_remove(struct hid_device *hdev)
|
|
+{
|
|
+ struct ally_x_device *ally_x = drvdata.ally_x;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&ally_x->lock, flags);
|
|
+ ally_x->output_worker_initialized = false;
|
|
+ spin_unlock_irqrestore(&ally_x->lock, flags);
|
|
+ cancel_work_sync(&ally_x->output_worker);
|
|
+ sysfs_remove_file(&hdev->dev.kobj, &dev_attr_ally_x_qam_mode.attr);
|
|
+}
|
|
+
|
|
/**************************************************************************************************/
|
|
/* ROG Ally LED control */
|
|
/**************************************************************************************************/
|
|
@@ -378,6 +727,24 @@ static void ally_rgb_remove(struct hid_device *hdev)
|
|
/* ROG Ally driver init */
|
|
/**************************************************************************************************/
|
|
|
|
+static int ally_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
|
|
+ int size)
|
|
+{
|
|
+ struct ally_x_device *ally_x = drvdata.ally_x;
|
|
+
|
|
+ if (ally_x) {
|
|
+ if ((hdev->bus == BUS_USB && report->id == ALLY_X_INPUT_REPORT_USB &&
|
|
+ size == ALLY_X_INPUT_REPORT_USB_SIZE) ||
|
|
+ (data[0] == 0x5A)) {
|
|
+ ally_x_raw_event(ally_x, report, data, size);
|
|
+ } else {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/*
|
|
* We don't care about any other part of the string except the version section.
|
|
* Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01
|
|
@@ -493,7 +860,8 @@ static int ally_hid_probe(struct hid_device *hdev, const struct hid_device_id *_
|
|
if (ep < 0)
|
|
return ep;
|
|
|
|
- if (ep != ROG_ALLY_CFG_INTF_IN)
|
|
+ if (ep != ROG_ALLY_CFG_INTF_IN &&
|
|
+ ep != ROG_ALLY_X_INTF_IN)
|
|
return -ENODEV;
|
|
|
|
ret = hid_parse(hdev);
|
|
@@ -536,6 +904,17 @@ static int ally_hid_probe(struct hid_device *hdev, const struct hid_device_id *_
|
|
goto err_close;
|
|
}
|
|
|
|
+ /* May or may not exist */
|
|
+ if (ep == ROG_ALLY_X_INTF_IN) {
|
|
+ drvdata.ally_x = ally_x_create(hdev);
|
|
+ if (IS_ERR(drvdata.ally_x)) {
|
|
+ hid_err(hdev, "Failed to create Ally X gamepad.\n");
|
|
+ drvdata.ally_x = NULL;
|
|
+ goto err_close;
|
|
+ }
|
|
+ hid_info(hdev, "Created Ally X controller.\n");
|
|
+ }
|
|
+
|
|
return 0;
|
|
|
|
err_close:
|
|
@@ -550,6 +929,9 @@ static void ally_hid_remove(struct hid_device *hdev)
|
|
if (drvdata.led_rgb_dev)
|
|
ally_rgb_remove(hdev);
|
|
|
|
+ if (drvdata.ally_x)
|
|
+ ally_x_remove(hdev);
|
|
+
|
|
hid_hw_close(hdev);
|
|
hid_hw_stop(hdev);
|
|
}
|
|
@@ -602,6 +984,7 @@ static struct hid_driver
|
|
.id_table = rog_ally_devices,
|
|
.probe = ally_hid_probe,
|
|
.remove = ally_hid_remove,
|
|
+ .raw_event = ally_raw_event,
|
|
/* HID is the better place for resume functions, not pm_ops */
|
|
.resume = ally_hid_resume,
|
|
.reset_resume = ally_hid_reset_resume,
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index eb8617c80c2a..458d02996bca 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -36,3 +36,8 @@ enum xpad_cmd_len {
|
|
xpad_cmd_len_response_curve = 0x09,
|
|
xpad_cmd_len_adz = 0x02,
|
|
};
|
|
+
|
|
+/* required so we can have nested attributes with same name but different functions */
|
|
+#define ALLY_DEVICE_ATTR_RW(_name, _sysfs_name) \
|
|
+ struct device_attribute dev_attr_##_name = \
|
|
+ __ATTR(_sysfs_name, 0644, _name##_show, _name##_store)
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 146afa6c8d47142eff2e0de7dee7c4301f77aeae Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Wed, 2 Oct 2024 23:51:36 +1300
|
|
Subject: [PATCH 09/28] hid-asus-ally: initial gamepad configuration
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 246 +++++++++++++++++++++++++++++++++++-
|
|
drivers/hid/hid-asus-ally.h | 38 +++---
|
|
2 files changed, 266 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 3d950535c3e2..822139e2a5c0 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -10,7 +10,6 @@
|
|
#include <linux/platform_device.h>
|
|
#include "linux/pm.h"
|
|
#include "linux/slab.h"
|
|
-#include "linux/stddef.h"
|
|
#include <linux/hid.h>
|
|
#include <linux/types.h>
|
|
#include <linux/usb.h>
|
|
@@ -39,6 +38,9 @@
|
|
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
|
#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
|
|
|
+#define BTN_DATA_LEN 11;
|
|
+#define BTN_CODE_BYTES_LEN 8
|
|
+
|
|
static const u8 EC_INIT_STRING[] = { 0x5A, 'A', 'S', 'U', 'S', ' ', 'T', 'e','c', 'h', '.', 'I', 'n', 'c', '.', '\0' };
|
|
static const u8 EC_MODE_LED_APPLY[] = { 0x5A, 0xB4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
static const u8 EC_MODE_LED_SET[] = { 0x5A, 0xB5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
@@ -50,6 +52,58 @@ static const struct hid_device_id rog_ally_devices[] = {
|
|
{}
|
|
};
|
|
|
|
+struct btn_code_map {
|
|
+ u64 code;
|
|
+ const char *name;
|
|
+};
|
|
+
|
|
+/* byte_array must be >= 8 in length */
|
|
+static void btn_code_to_byte_array(u64 keycode, u8 *byte_array)
|
|
+{
|
|
+ /* Convert the u64 to bytes[8] */
|
|
+ for (int i = 0; i < 8; ++i) {
|
|
+ byte_array[i] = (keycode >> (56 - 8 * i)) & 0xFF;
|
|
+ }
|
|
+}
|
|
+
|
|
+struct btn_data {
|
|
+ u64 button;
|
|
+ u64 macro;
|
|
+};
|
|
+
|
|
+struct btn_mapping {
|
|
+ struct btn_data btn_a;
|
|
+ struct btn_data btn_b;
|
|
+ struct btn_data btn_x;
|
|
+ struct btn_data btn_y;
|
|
+ struct btn_data btn_lb;
|
|
+ struct btn_data btn_rb;
|
|
+ struct btn_data btn_ls;
|
|
+ struct btn_data btn_rs;
|
|
+ struct btn_data btn_lt;
|
|
+ struct btn_data btn_rt;
|
|
+ struct btn_data dpad_up;
|
|
+ struct btn_data dpad_down;
|
|
+ struct btn_data dpad_left;
|
|
+ struct btn_data dpad_right;
|
|
+ struct btn_data btn_view;
|
|
+ struct btn_data btn_menu;
|
|
+ struct btn_data btn_m1;
|
|
+ struct btn_data btn_m2;
|
|
+};
|
|
+
|
|
+/* ROG Ally has many settings related to the gamepad, all using the same n-key endpoint */
|
|
+struct ally_gamepad_cfg {
|
|
+ struct hid_device *hdev;
|
|
+ struct input_dev *input;
|
|
+
|
|
+ enum xpad_mode mode;
|
|
+ /*
|
|
+ * index: [mode]
|
|
+ */
|
|
+ struct btn_mapping *key_mapping[xpad_mode_mouse];
|
|
+};
|
|
+
|
|
/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
static const int hat_values[][2] = {
|
|
{ 0, 0 }, { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 },
|
|
@@ -120,6 +174,7 @@ struct ally_rgb_data {
|
|
static struct ally_drvdata {
|
|
struct hid_device *hdev;
|
|
struct ally_x_device *ally_x;
|
|
+ struct ally_gamepad_cfg *gamepad_cfg;
|
|
struct ally_rgb_dev *led_rgb_dev;
|
|
struct ally_rgb_data led_rgb_data;
|
|
} drvdata;
|
|
@@ -174,6 +229,172 @@ static u8 get_endpoint_address(struct hid_device *hdev)
|
|
return -ENODEV;
|
|
}
|
|
|
|
+/**************************************************************************************************/
|
|
+/* ROG Ally gamepad configuration */
|
|
+/**************************************************************************************************/
|
|
+
|
|
+/* This should be called before any attempts to set device functions */
|
|
+static int ally_gamepad_check_ready(struct hid_device *hdev)
|
|
+{
|
|
+ int ret, count;
|
|
+ u8 *hidbuf;
|
|
+
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = 0;
|
|
+ for (count = 0; count < READY_MAX_TRIES; count++) {
|
|
+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
|
|
+ hidbuf[2] = xpad_cmd_check_ready;
|
|
+ hidbuf[3] = 01;
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ hid_dbg(hdev, "ROG Ally check failed set report: %d\n", ret);
|
|
+
|
|
+ hidbuf[0] = hidbuf[1] = hidbuf[2] = hidbuf[3] = 0;
|
|
+ ret = asus_dev_get_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ hid_dbg(hdev, "ROG Ally check failed get report: %d\n", ret);
|
|
+
|
|
+ ret = hidbuf[2] == xpad_cmd_check_ready;
|
|
+ if (ret)
|
|
+ break;
|
|
+ usleep_range(
|
|
+ 1000,
|
|
+ 2000); /* don't spam the entire loop in less than USB response time */
|
|
+ }
|
|
+
|
|
+ if (count == READY_MAX_TRIES)
|
|
+ hid_warn(hdev, "ROG Ally never responded with a ready\n");
|
|
+
|
|
+ kfree(hidbuf);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* A HID packet conatins mappings for two buttons: btn1, btn1_macro, btn2, btn2_macro */
|
|
+static void _btn_pair_to_hid_pkt(struct ally_gamepad_cfg *ally_cfg,
|
|
+ enum btn_pair_index pair,
|
|
+ struct btn_data *btn1, struct btn_data *btn2,
|
|
+ u8 *out, int out_len)
|
|
+{
|
|
+ int start = 5;
|
|
+
|
|
+ out[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ out[1] = FEATURE_ROG_ALLY_CODE_PAGE;
|
|
+ out[2] = xpad_cmd_set_mapping;
|
|
+ out[3] = pair;
|
|
+ out[4] = xpad_cmd_len_mapping;
|
|
+
|
|
+ btn_code_to_byte_array(btn1->button, &out[start]);
|
|
+ start += BTN_DATA_LEN;
|
|
+ btn_code_to_byte_array(btn1->macro, &out[start]);
|
|
+ start += BTN_DATA_LEN;
|
|
+ btn_code_to_byte_array(btn2->button, &out[start]);
|
|
+ start += BTN_DATA_LEN;
|
|
+ btn_code_to_byte_array(btn2->macro, &out[start]);
|
|
+ //print_hex_dump(KERN_DEBUG, "byte_array: ", DUMP_PREFIX_OFFSET, 64, 1, out, 64, false);
|
|
+}
|
|
+
|
|
+/* Apply the mapping pair to the device */
|
|
+static int _gamepad_apply_btn_pair(struct hid_device *hdev, struct ally_gamepad_cfg *ally_cfg,
|
|
+ enum btn_pair_index btn_pair)
|
|
+{
|
|
+ u8 mode = ally_cfg->mode - 1;
|
|
+ struct btn_data *btn1, *btn2;
|
|
+ u8 *hidbuf;
|
|
+ int ret;
|
|
+
|
|
+ ret = ally_gamepad_check_ready(hdev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ switch (btn_pair) {
|
|
+ case btn_pair_m1_m2:
|
|
+ btn1 = &ally_cfg->key_mapping[mode]->btn_m1;
|
|
+ btn2 = &ally_cfg->key_mapping[mode]->btn_m2;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ _btn_pair_to_hid_pkt(ally_cfg, btn_pair, btn1, btn2, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+
|
|
+ kfree(hidbuf);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg;
|
|
+ struct input_dev *input_dev;
|
|
+ int err;
|
|
+
|
|
+ ally_cfg = devm_kzalloc(&hdev->dev, sizeof(*ally_cfg), GFP_KERNEL);
|
|
+ if (!ally_cfg)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ ally_cfg->hdev = hdev;
|
|
+ // Allocate memory for each mode's `btn_mapping`
|
|
+ ally_cfg->mode = xpad_mode_game;
|
|
+ for (int i = 0; i < xpad_mode_mouse; i++) {
|
|
+ ally_cfg->key_mapping[i] = devm_kzalloc(&hdev->dev, sizeof(struct btn_mapping), GFP_KERNEL);
|
|
+ if (!ally_cfg->key_mapping[i]) {
|
|
+ err = -ENOMEM;
|
|
+ goto free_key_mappings;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ input_dev = devm_input_allocate_device(&hdev->dev);
|
|
+ if (!input_dev) {
|
|
+ err = -ENOMEM;
|
|
+ goto free_ally_cfg;
|
|
+ }
|
|
+
|
|
+ input_dev->id.bustype = hdev->bus;
|
|
+ input_dev->id.vendor = hdev->vendor;
|
|
+ input_dev->id.product = hdev->product;
|
|
+ input_dev->id.version = hdev->version;
|
|
+ input_dev->uniq = hdev->uniq;
|
|
+ input_dev->name = "ASUS ROG Ally Config";
|
|
+ input_set_capability(input_dev, EV_KEY, KEY_PROG1);
|
|
+ input_set_capability(input_dev, EV_KEY, KEY_F16);
|
|
+ input_set_capability(input_dev, EV_KEY, KEY_F17);
|
|
+ input_set_capability(input_dev, EV_KEY, KEY_F18);
|
|
+ input_set_drvdata(input_dev, hdev);
|
|
+
|
|
+ err = input_register_device(input_dev);
|
|
+ if (err)
|
|
+ goto free_input_dev;
|
|
+ ally_cfg->input = input_dev;
|
|
+
|
|
+ /* ignore all errors for this as they are related to USB HID I/O */
|
|
+ ally_cfg->key_mapping[ally_cfg->mode - 1]->btn_m1.button = BTN_KB_M1;
|
|
+ ally_cfg->key_mapping[ally_cfg->mode - 1]->btn_m2.button = BTN_KB_M2;
|
|
+ _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_m1_m2);
|
|
+
|
|
+ return ally_cfg;
|
|
+
|
|
+free_input_dev:
|
|
+ devm_kfree(&hdev->dev, input_dev);
|
|
+
|
|
+free_key_mappings:
|
|
+ for (int i = 0; i < xpad_mode_mouse; i++) {
|
|
+ if (ally_cfg->key_mapping[i])
|
|
+ devm_kfree(&hdev->dev, ally_cfg->key_mapping[i]);
|
|
+ }
|
|
+
|
|
+free_ally_cfg:
|
|
+ devm_kfree(&hdev->dev, ally_cfg);
|
|
+ return ERR_PTR(err);
|
|
+}
|
|
+
|
|
/**************************************************************************************************/
|
|
/* ROG Ally gamepad i/o and force-feedback */
|
|
/**************************************************************************************************/
|
|
@@ -730,6 +951,7 @@ static void ally_rgb_remove(struct hid_device *hdev)
|
|
static int ally_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
|
|
int size)
|
|
{
|
|
+ struct ally_gamepad_cfg *cfg = drvdata.gamepad_cfg;
|
|
struct ally_x_device *ally_x = drvdata.ally_x;
|
|
|
|
if (ally_x) {
|
|
@@ -742,6 +964,14 @@ static int ally_raw_event(struct hid_device *hdev, struct hid_report *report, u8
|
|
}
|
|
}
|
|
|
|
+ if (cfg && !ally_x) {
|
|
+ input_report_key(cfg->input, KEY_PROG1, data[1] == 0x38);
|
|
+ input_report_key(cfg->input, KEY_F16, data[1] == 0xA6);
|
|
+ input_report_key(cfg->input, KEY_F17, data[1] == 0xA7);
|
|
+ input_report_key(cfg->input, KEY_F18, data[1] == 0xA8);
|
|
+ input_sync(cfg->input);
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -900,7 +1130,13 @@ static int ally_hid_probe(struct hid_device *hdev, const struct hid_device_id *_
|
|
else
|
|
hid_info(hdev, "Created Ally RGB LED controls.\n");
|
|
|
|
- if (IS_ERR(drvdata.led_rgb_dev))
|
|
+ drvdata.gamepad_cfg = ally_gamepad_cfg_create(hdev);
|
|
+ if (IS_ERR(drvdata.gamepad_cfg))
|
|
+ hid_err(hdev, "Failed to create Ally gamepad attributes.\n");
|
|
+ else
|
|
+ hid_info(hdev, "Created Ally gamepad attributes.\n");
|
|
+
|
|
+ if (IS_ERR(drvdata.led_rgb_dev) && IS_ERR(drvdata.gamepad_cfg))
|
|
goto err_close;
|
|
}
|
|
|
|
@@ -913,6 +1149,12 @@ static int ally_hid_probe(struct hid_device *hdev, const struct hid_device_id *_
|
|
goto err_close;
|
|
}
|
|
hid_info(hdev, "Created Ally X controller.\n");
|
|
+
|
|
+ // Not required since we send this inputs ep through the gamepad input dev
|
|
+ if (drvdata.gamepad_cfg && drvdata.gamepad_cfg->input) {
|
|
+ input_unregister_device(drvdata.gamepad_cfg->input);
|
|
+ hid_info(hdev, "Ally X removed unrequired input dev.\n");
|
|
+ }
|
|
}
|
|
|
|
return 0;
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index 458d02996bca..2b298ad4da0e 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -8,35 +8,41 @@
|
|
#include <linux/hid.h>
|
|
#include <linux/types.h>
|
|
|
|
+/*
|
|
+ * the xpad_mode is used inside the mode setting packet and is used
|
|
+ * for indexing (xpad_mode - 1)
|
|
+ */
|
|
+enum xpad_mode {
|
|
+ xpad_mode_game = 0x01,
|
|
+ xpad_mode_wasd = 0x02,
|
|
+ xpad_mode_mouse = 0x03,
|
|
+};
|
|
+
|
|
/* the xpad_cmd determines which feature is set or queried */
|
|
enum xpad_cmd {
|
|
- xpad_cmd_set_mode = 0x01,
|
|
xpad_cmd_set_mapping = 0x02,
|
|
- xpad_cmd_set_js_dz = 0x04, /* deadzones */
|
|
- xpad_cmd_set_tr_dz = 0x05, /* deadzones */
|
|
- xpad_cmd_set_vibe_intensity = 0x06,
|
|
xpad_cmd_set_leds = 0x08,
|
|
xpad_cmd_check_ready = 0x0A,
|
|
- xpad_cmd_set_calibration = 0x0D,
|
|
- xpad_cmd_set_turbo = 0x0F,
|
|
- xpad_cmd_set_response_curve = 0x13,
|
|
- xpad_cmd_set_adz = 0x18,
|
|
};
|
|
|
|
/* the xpad_cmd determines which feature is set or queried */
|
|
enum xpad_cmd_len {
|
|
- xpad_cmd_len_mode = 0x01,
|
|
xpad_cmd_len_mapping = 0x2c,
|
|
- xpad_cmd_len_deadzone = 0x04,
|
|
- xpad_cmd_len_vibe_intensity = 0x02,
|
|
xpad_cmd_len_leds = 0x0C,
|
|
- xpad_cmd_len_calibration2 = 0x01,
|
|
- xpad_cmd_len_calibration3 = 0x01,
|
|
- xpad_cmd_len_turbo = 0x20,
|
|
- xpad_cmd_len_response_curve = 0x09,
|
|
- xpad_cmd_len_adz = 0x02,
|
|
};
|
|
|
|
+/* Values correspond to the actual HID byte value required */
|
|
+enum btn_pair_index {
|
|
+ btn_pair_m1_m2 = 0x08,
|
|
+};
|
|
+
|
|
+#define BTN_KB_M2 0x02008E0000000000
|
|
+#define BTN_KB_M1 0x02008F0000000000
|
|
+
|
|
+#define ALLY_DEVICE_ATTR_WO(_name, _sysfs_name) \
|
|
+ struct device_attribute dev_attr_##_name = \
|
|
+ __ATTR(_sysfs_name, 0200, NULL, _name##_store)
|
|
+
|
|
/* required so we can have nested attributes with same name but different functions */
|
|
#define ALLY_DEVICE_ATTR_RW(_name, _sysfs_name) \
|
|
struct device_attribute dev_attr_##_name = \
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 9bbfc9800a5489d1d6585d2174e2b34660026e1f Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sat, 5 Oct 2024 14:58:33 +1300
|
|
Subject: [PATCH 10/28] hid-asus-ally: add button remap attributes
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 393 ++++++++++++++++++++++++++++++++++--
|
|
drivers/hid/hid-asus-ally.h | 211 +++++++++++++++++++
|
|
2 files changed, 586 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 822139e2a5c0..884cb688197e 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -57,6 +57,152 @@ struct btn_code_map {
|
|
const char *name;
|
|
};
|
|
|
|
+static const struct btn_code_map ally_btn_codes[] = {
|
|
+ { 0, "NONE" },
|
|
+ /* Gamepad button codes */
|
|
+ { BTN_PAD_A, "PAD_A" },
|
|
+ { BTN_PAD_B, "PAD_B" },
|
|
+ { BTN_PAD_X, "PAD_X" },
|
|
+ { BTN_PAD_Y, "PAD_Y" },
|
|
+ { BTN_PAD_LB, "PAD_LB" },
|
|
+ { BTN_PAD_RB, "PAD_RB" },
|
|
+ { BTN_PAD_LS, "PAD_LS" },
|
|
+ { BTN_PAD_RS, "PAD_RS" },
|
|
+ { BTN_PAD_DPAD_UP, "PAD_DPAD_UP" },
|
|
+ { BTN_PAD_DPAD_DOWN, "PAD_DPAD_DOWN" },
|
|
+ { BTN_PAD_DPAD_LEFT, "PAD_DPAD_LEFT" },
|
|
+ { BTN_PAD_DPAD_RIGHT, "PAD_DPAD_RIGHT" },
|
|
+ { BTN_PAD_VIEW, "PAD_VIEW" },
|
|
+ { BTN_PAD_MENU, "PAD_MENU" },
|
|
+ { BTN_PAD_XBOX, "PAD_XBOX" },
|
|
+
|
|
+ /* Triggers mapped to keyboard codes */
|
|
+ { BTN_KB_M2, "KB_M2" },
|
|
+ { BTN_KB_M1, "KB_M1" },
|
|
+ { BTN_KB_ESC, "KB_ESC" },
|
|
+ { BTN_KB_F1, "KB_F1" },
|
|
+ { BTN_KB_F2, "KB_F2" },
|
|
+ { BTN_KB_F3, "KB_F3" },
|
|
+ { BTN_KB_F4, "KB_F4" },
|
|
+ { BTN_KB_F5, "KB_F5" },
|
|
+ { BTN_KB_F6, "KB_F6" },
|
|
+ { BTN_KB_F7, "KB_F7" },
|
|
+ { BTN_KB_F8, "KB_F8" },
|
|
+ { BTN_KB_F9, "KB_F9" },
|
|
+ { BTN_KB_F10, "KB_F10" },
|
|
+ { BTN_KB_F11, "KB_F11" },
|
|
+ { BTN_KB_F12, "KB_F12" },
|
|
+ { BTN_KB_F14, "KB_F14" },
|
|
+ { BTN_KB_F15, "KB_F15" },
|
|
+ { BTN_KB_BACKTICK, "KB_BACKTICK" },
|
|
+ { BTN_KB_1, "KB_1" },
|
|
+ { BTN_KB_2, "KB_2" },
|
|
+ { BTN_KB_3, "KB_3" },
|
|
+ { BTN_KB_4, "KB_4" },
|
|
+ { BTN_KB_5, "KB_5" },
|
|
+ { BTN_KB_6, "KB_6" },
|
|
+ { BTN_KB_7, "KB_7" },
|
|
+ { BTN_KB_8, "KB_8" },
|
|
+ { BTN_KB_9, "KB_9" },
|
|
+ { BTN_KB_0, "KB_0" },
|
|
+ { BTN_KB_HYPHEN, "KB_HYPHEN" },
|
|
+ { BTN_KB_EQUALS, "KB_EQUALS" },
|
|
+ { BTN_KB_BACKSPACE, "KB_BACKSPACE" },
|
|
+ { BTN_KB_TAB, "KB_TAB" },
|
|
+ { BTN_KB_Q, "KB_Q" },
|
|
+ { BTN_KB_W, "KB_W" },
|
|
+ { BTN_KB_E, "KB_E" },
|
|
+ { BTN_KB_R, "KB_R" },
|
|
+ { BTN_KB_T, "KB_T" },
|
|
+ { BTN_KB_Y, "KB_Y" },
|
|
+ { BTN_KB_U, "KB_U" },
|
|
+ { BTN_KB_O, "KB_O" },
|
|
+ { BTN_KB_P, "KB_P" },
|
|
+ { BTN_KB_LBRACKET, "KB_LBRACKET" },
|
|
+ { BTN_KB_RBRACKET, "KB_RBRACKET" },
|
|
+ { BTN_KB_BACKSLASH, "KB_BACKSLASH" },
|
|
+ { BTN_KB_CAPS, "KB_CAPS" },
|
|
+ { BTN_KB_A, "KB_A" },
|
|
+ { BTN_KB_S, "KB_S" },
|
|
+ { BTN_KB_D, "KB_D" },
|
|
+ { BTN_KB_F, "KB_F" },
|
|
+ { BTN_KB_G, "KB_G" },
|
|
+ { BTN_KB_H, "KB_H" },
|
|
+ { BTN_KB_J, "KB_J" },
|
|
+ { BTN_KB_K, "KB_K" },
|
|
+ { BTN_KB_L, "KB_L" },
|
|
+ { BTN_KB_SEMI, "KB_SEMI" },
|
|
+ { BTN_KB_QUOTE, "KB_QUOTE" },
|
|
+ { BTN_KB_RET, "KB_RET" },
|
|
+ { BTN_KB_LSHIFT, "KB_LSHIFT" },
|
|
+ { BTN_KB_Z, "KB_Z" },
|
|
+ { BTN_KB_X, "KB_X" },
|
|
+ { BTN_KB_C, "KB_C" },
|
|
+ { BTN_KB_V, "KB_V" },
|
|
+ { BTN_KB_B, "KB_B" },
|
|
+ { BTN_KB_N, "KB_N" },
|
|
+ { BTN_KB_M, "KB_M" },
|
|
+ { BTN_KB_COMMA, "KB_COMMA" },
|
|
+ { BTN_KB_PERIOD, "KB_PERIOD" },
|
|
+ { BTN_KB_RSHIFT, "KB_RSHIFT" },
|
|
+ { BTN_KB_LCTL, "KB_LCTL" },
|
|
+ { BTN_KB_META, "KB_META" },
|
|
+ { BTN_KB_LALT, "KB_LALT" },
|
|
+ { BTN_KB_SPACE, "KB_SPACE" },
|
|
+ { BTN_KB_RALT, "KB_RALT" },
|
|
+ { BTN_KB_MENU, "KB_MENU" },
|
|
+ { BTN_KB_RCTL, "KB_RCTL" },
|
|
+ { BTN_KB_PRNTSCN, "KB_PRNTSCN" },
|
|
+ { BTN_KB_SCRLCK, "KB_SCRLCK" },
|
|
+ { BTN_KB_PAUSE, "KB_PAUSE" },
|
|
+ { BTN_KB_INS, "KB_INS" },
|
|
+ { BTN_KB_HOME, "KB_HOME" },
|
|
+ { BTN_KB_PGUP, "KB_PGUP" },
|
|
+ { BTN_KB_DEL, "KB_DEL" },
|
|
+ { BTN_KB_END, "KB_END" },
|
|
+ { BTN_KB_PGDWN, "KB_PGDWN" },
|
|
+ { BTN_KB_UP_ARROW, "KB_UP_ARROW" },
|
|
+ { BTN_KB_DOWN_ARROW, "KB_DOWN_ARROW" },
|
|
+ { BTN_KB_LEFT_ARROW, "KB_LEFT_ARROW" },
|
|
+ { BTN_KB_RIGHT_ARROW, "KB_RIGHT_ARROW" },
|
|
+
|
|
+ /* Numpad mappings */
|
|
+ { BTN_NUMPAD_LOCK, "NUMPAD_LOCK" },
|
|
+ { BTN_NUMPAD_FWDSLASH, "NUMPAD_FWDSLASH" },
|
|
+ { BTN_NUMPAD_ASTERISK, "NUMPAD_ASTERISK" },
|
|
+ { BTN_NUMPAD_HYPHEN, "NUMPAD_HYPHEN" },
|
|
+ { BTN_NUMPAD_0, "NUMPAD_0" },
|
|
+ { BTN_NUMPAD_1, "NUMPAD_1" },
|
|
+ { BTN_NUMPAD_2, "NUMPAD_2" },
|
|
+ { BTN_NUMPAD_3, "NUMPAD_3" },
|
|
+ { BTN_NUMPAD_4, "NUMPAD_4" },
|
|
+ { BTN_NUMPAD_5, "NUMPAD_5" },
|
|
+ { BTN_NUMPAD_6, "NUMPAD_6" },
|
|
+ { BTN_NUMPAD_7, "NUMPAD_7" },
|
|
+ { BTN_NUMPAD_8, "NUMPAD_8" },
|
|
+ { BTN_NUMPAD_9, "NUMPAD_9" },
|
|
+ { BTN_NUMPAD_PLUS, "NUMPAD_PLUS" },
|
|
+ { BTN_NUMPAD_ENTER, "NUMPAD_ENTER" },
|
|
+ { BTN_NUMPAD_PERIOD, "NUMPAD_PERIOD" },
|
|
+
|
|
+ /* Mouse mappings */
|
|
+ { BTN_MOUSE_LCLICK, "MOUSE_LCLICK" },
|
|
+ { BTN_MOUSE_RCLICK, "MOUSE_RCLICK" },
|
|
+ { BTN_MOUSE_MCLICK, "MOUSE_MCLICK" },
|
|
+ { BTN_MOUSE_WHEEL_UP, "MOUSE_WHEEL_UP" },
|
|
+ { BTN_MOUSE_WHEEL_DOWN, "MOUSE_WHEEL_DOWN" },
|
|
+
|
|
+ /* Media mappings */
|
|
+ { BTN_MEDIA_SCREENSHOT, "MEDIA_SCREENSHOT" },
|
|
+ { BTN_MEDIA_SHOW_KEYBOARD, "MEDIA_SHOW_KEYBOARD" },
|
|
+ { BTN_MEDIA_SHOW_DESKTOP, "MEDIA_SHOW_DESKTOP" },
|
|
+ { BTN_MEDIA_START_RECORDING, "MEDIA_START_RECORDING" },
|
|
+ { BTN_MEDIA_MIC_OFF, "MEDIA_MIC_OFF" },
|
|
+ { BTN_MEDIA_VOL_DOWN, "MEDIA_VOL_DOWN" },
|
|
+ { BTN_MEDIA_VOL_UP, "MEDIA_VOL_UP" },
|
|
+};
|
|
+static const size_t keymap_len = ARRAY_SIZE(ally_btn_codes);
|
|
+
|
|
/* byte_array must be >= 8 in length */
|
|
static void btn_code_to_byte_array(u64 keycode, u8 *byte_array)
|
|
{
|
|
@@ -66,6 +212,27 @@ static void btn_code_to_byte_array(u64 keycode, u8 *byte_array)
|
|
}
|
|
}
|
|
|
|
+static u64 name_to_btn(const char *name)
|
|
+{
|
|
+ int len = strcspn(name, "\n");
|
|
+ for (size_t i = 0; i < keymap_len; ++i) {
|
|
+ if (strncmp(ally_btn_codes[i].name, name, len) == 0) {
|
|
+ return ally_btn_codes[i].code;
|
|
+ }
|
|
+ }
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static const char* btn_to_name(u64 key)
|
|
+{
|
|
+ for (size_t i = 0; i < keymap_len; ++i) {
|
|
+ if (ally_btn_codes[i].code == key) {
|
|
+ return ally_btn_codes[i].name;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
struct btn_data {
|
|
u64 button;
|
|
u64 macro;
|
|
@@ -101,7 +268,7 @@ struct ally_gamepad_cfg {
|
|
/*
|
|
* index: [mode]
|
|
*/
|
|
- struct btn_mapping *key_mapping[xpad_mode_mouse];
|
|
+ struct btn_mapping key_mapping[xpad_mode_mouse];
|
|
};
|
|
|
|
/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
@@ -315,9 +482,41 @@ static int _gamepad_apply_btn_pair(struct hid_device *hdev, struct ally_gamepad_
|
|
return -ENOMEM;
|
|
|
|
switch (btn_pair) {
|
|
+ case btn_pair_dpad_u_d:
|
|
+ btn1 = &ally_cfg->key_mapping[mode].dpad_up;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].dpad_down;
|
|
+ break;
|
|
+ case btn_pair_dpad_l_r:
|
|
+ btn1 = &ally_cfg->key_mapping[mode].dpad_left;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].dpad_right;
|
|
+ break;
|
|
+ case btn_pair_ls_rs:
|
|
+ btn1 = &ally_cfg->key_mapping[mode].btn_ls;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].btn_rs;
|
|
+ break;
|
|
+ case btn_pair_lb_rb:
|
|
+ btn1 = &ally_cfg->key_mapping[mode].btn_lb;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].btn_rb;
|
|
+ break;
|
|
+ case btn_pair_lt_rt:
|
|
+ btn1 = &ally_cfg->key_mapping[mode].btn_lt;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].btn_rt;
|
|
+ break;
|
|
+ case btn_pair_a_b:
|
|
+ btn1 = &ally_cfg->key_mapping[mode].btn_a;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].btn_b;
|
|
+ break;
|
|
+ case btn_pair_x_y:
|
|
+ btn1 = &ally_cfg->key_mapping[mode].btn_x;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].btn_y;
|
|
+ break;
|
|
+ case btn_pair_view_menu:
|
|
+ btn1 = &ally_cfg->key_mapping[mode].btn_view;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].btn_menu;
|
|
+ break;
|
|
case btn_pair_m1_m2:
|
|
- btn1 = &ally_cfg->key_mapping[mode]->btn_m1;
|
|
- btn2 = &ally_cfg->key_mapping[mode]->btn_m2;
|
|
+ btn1 = &ally_cfg->key_mapping[mode].btn_m1;
|
|
+ btn2 = &ally_cfg->key_mapping[mode].btn_m2;
|
|
break;
|
|
default:
|
|
break;
|
|
@@ -331,6 +530,157 @@ static int _gamepad_apply_btn_pair(struct hid_device *hdev, struct ally_gamepad_
|
|
return ret;
|
|
}
|
|
|
|
+static ssize_t _gamepad_apply_all(struct hid_device *hdev, struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_dpad_u_d);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_dpad_l_r);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_ls_rs);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_lb_rb);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_a_b);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_x_y);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_view_menu);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_m1_m2);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_lt_rt);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t gamepad_apply_all_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+ struct hid_device *hdev = to_hid_device(dev);
|
|
+ int ret;
|
|
+
|
|
+ if (!drvdata.gamepad_cfg)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = _gamepad_apply_all(hdev, ally_cfg);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+ALLY_DEVICE_ATTR_WO(gamepad_apply_all, apply_all);
|
|
+
|
|
+/* button map attributes, regular and macro*/
|
|
+ALLY_BTN_MAPPING(m1, btn_m1);
|
|
+ALLY_BTN_MAPPING(m2, btn_m2);
|
|
+ALLY_BTN_MAPPING(a, btn_a);
|
|
+ALLY_BTN_MAPPING(b, btn_b);
|
|
+ALLY_BTN_MAPPING(x, btn_x);
|
|
+ALLY_BTN_MAPPING(y, btn_y);
|
|
+ALLY_BTN_MAPPING(lb, btn_lb);
|
|
+ALLY_BTN_MAPPING(rb, btn_rb);
|
|
+ALLY_BTN_MAPPING(ls, btn_ls);
|
|
+ALLY_BTN_MAPPING(rs, btn_rs);
|
|
+ALLY_BTN_MAPPING(lt, btn_lt);
|
|
+ALLY_BTN_MAPPING(rt, btn_rt);
|
|
+ALLY_BTN_MAPPING(dpad_u, dpad_up);
|
|
+ALLY_BTN_MAPPING(dpad_d, dpad_down);
|
|
+ALLY_BTN_MAPPING(dpad_l, dpad_left);
|
|
+ALLY_BTN_MAPPING(dpad_r, dpad_right);
|
|
+ALLY_BTN_MAPPING(view, btn_view);
|
|
+ALLY_BTN_MAPPING(menu, btn_menu);
|
|
+
|
|
+static void _gamepad_set_xpad_default(struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ struct btn_mapping *map = &ally_cfg->key_mapping[ally_cfg->mode - 1];
|
|
+ map->btn_m1.button = BTN_KB_M1;
|
|
+ map->btn_m2.button = BTN_KB_M2;
|
|
+ map->btn_a.button = BTN_PAD_A;
|
|
+ map->btn_b.button = BTN_PAD_B;
|
|
+ map->btn_x.button = BTN_PAD_X;
|
|
+ map->btn_y.button = BTN_PAD_Y;
|
|
+ map->btn_lb.button = BTN_PAD_LB;
|
|
+ map->btn_rb.button = BTN_PAD_RB;
|
|
+ map->btn_lt.button = BTN_PAD_LT;
|
|
+ map->btn_rt.button = BTN_PAD_RT;
|
|
+ map->btn_ls.button = BTN_PAD_LS;
|
|
+ map->btn_rs.button = BTN_PAD_RS;
|
|
+ map->dpad_up.button = BTN_PAD_DPAD_UP;
|
|
+ map->dpad_down.button = BTN_PAD_DPAD_DOWN;
|
|
+ map->dpad_left.button = BTN_PAD_DPAD_LEFT;
|
|
+ map->dpad_right.button = BTN_PAD_DPAD_RIGHT;
|
|
+ map->btn_view.button = BTN_PAD_VIEW;
|
|
+ map->btn_menu.button = BTN_PAD_MENU;
|
|
+}
|
|
+
|
|
+static ssize_t btn_mapping_reset_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+
|
|
+ if (!drvdata.gamepad_cfg)
|
|
+ return -ENODEV;
|
|
+
|
|
+ switch (ally_cfg->mode) {
|
|
+ case xpad_mode_game:
|
|
+ _gamepad_set_xpad_default(ally_cfg);
|
|
+ break;
|
|
+ default:
|
|
+ _gamepad_set_xpad_default(ally_cfg);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+ALLY_DEVICE_ATTR_WO(btn_mapping_reset, reset_btn_mapping);
|
|
+
|
|
+/* ROOT LEVEL ATTRS *******************************************************************************/
|
|
+static struct attribute *gamepad_device_attrs[] = {
|
|
+ &dev_attr_btn_mapping_reset.attr,
|
|
+ &dev_attr_gamepad_apply_all.attr,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static const struct attribute_group ally_controller_attr_group = {
|
|
+ .attrs = gamepad_device_attrs,
|
|
+};
|
|
+
|
|
+static const struct attribute_group *gamepad_device_attr_groups[] = {
|
|
+ &ally_controller_attr_group,
|
|
+ &btn_mapping_m1_attr_group,
|
|
+ &btn_mapping_m2_attr_group,
|
|
+ &btn_mapping_a_attr_group,
|
|
+ &btn_mapping_b_attr_group,
|
|
+ &btn_mapping_x_attr_group,
|
|
+ &btn_mapping_y_attr_group,
|
|
+ &btn_mapping_lb_attr_group,
|
|
+ &btn_mapping_rb_attr_group,
|
|
+ &btn_mapping_ls_attr_group,
|
|
+ &btn_mapping_rs_attr_group,
|
|
+ &btn_mapping_lt_attr_group,
|
|
+ &btn_mapping_rt_attr_group,
|
|
+ &btn_mapping_dpad_u_attr_group,
|
|
+ &btn_mapping_dpad_d_attr_group,
|
|
+ &btn_mapping_dpad_l_attr_group,
|
|
+ &btn_mapping_dpad_r_attr_group,
|
|
+ &btn_mapping_view_attr_group,
|
|
+ &btn_mapping_menu_attr_group,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
{
|
|
struct ally_gamepad_cfg *ally_cfg;
|
|
@@ -343,13 +693,6 @@ static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
ally_cfg->hdev = hdev;
|
|
// Allocate memory for each mode's `btn_mapping`
|
|
ally_cfg->mode = xpad_mode_game;
|
|
- for (int i = 0; i < xpad_mode_mouse; i++) {
|
|
- ally_cfg->key_mapping[i] = devm_kzalloc(&hdev->dev, sizeof(struct btn_mapping), GFP_KERNEL);
|
|
- if (!ally_cfg->key_mapping[i]) {
|
|
- err = -ENOMEM;
|
|
- goto free_key_mappings;
|
|
- }
|
|
- }
|
|
|
|
input_dev = devm_input_allocate_device(&hdev->dev);
|
|
if (!input_dev) {
|
|
@@ -375,26 +718,37 @@ static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
ally_cfg->input = input_dev;
|
|
|
|
/* ignore all errors for this as they are related to USB HID I/O */
|
|
- ally_cfg->key_mapping[ally_cfg->mode - 1]->btn_m1.button = BTN_KB_M1;
|
|
- ally_cfg->key_mapping[ally_cfg->mode - 1]->btn_m2.button = BTN_KB_M2;
|
|
+ _gamepad_set_xpad_default(ally_cfg);
|
|
+ ally_cfg->key_mapping[ally_cfg->mode - 1].btn_m1.button = BTN_KB_M1;
|
|
+ ally_cfg->key_mapping[ally_cfg->mode - 1].btn_m2.button = BTN_KB_M2;
|
|
_gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_m1_m2);
|
|
|
|
+ drvdata.gamepad_cfg = ally_cfg; // Must asign before attr group setup
|
|
+ if (sysfs_create_groups(&hdev->dev.kobj, gamepad_device_attr_groups)) {
|
|
+ err = -ENODEV;
|
|
+ goto unregister_input_dev;
|
|
+ }
|
|
+
|
|
return ally_cfg;
|
|
|
|
+unregister_input_dev:
|
|
+ input_unregister_device(input_dev);
|
|
+ ally_cfg->input = NULL; // Prevent double free when kfree(ally_cfg) happens
|
|
+
|
|
free_input_dev:
|
|
devm_kfree(&hdev->dev, input_dev);
|
|
|
|
-free_key_mappings:
|
|
- for (int i = 0; i < xpad_mode_mouse; i++) {
|
|
- if (ally_cfg->key_mapping[i])
|
|
- devm_kfree(&hdev->dev, ally_cfg->key_mapping[i]);
|
|
- }
|
|
-
|
|
free_ally_cfg:
|
|
devm_kfree(&hdev->dev, ally_cfg);
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
+static void ally_cfg_remove(struct hid_device *hdev)
|
|
+{
|
|
+ // __gamepad_set_mode(hdev, drvdata.gamepad_cfg, xpad_mode_mouse);
|
|
+ sysfs_remove_groups(&hdev->dev.kobj, gamepad_device_attr_groups);
|
|
+}
|
|
+
|
|
/**************************************************************************************************/
|
|
/* ROG Ally gamepad i/o and force-feedback */
|
|
/**************************************************************************************************/
|
|
@@ -1174,6 +1528,9 @@ static void ally_hid_remove(struct hid_device *hdev)
|
|
if (drvdata.ally_x)
|
|
ally_x_remove(hdev);
|
|
|
|
+ if (drvdata.gamepad_cfg)
|
|
+ ally_cfg_remove(hdev);
|
|
+
|
|
hid_hw_close(hdev);
|
|
hid_hw_stop(hdev);
|
|
}
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index 2b298ad4da0e..f985cbd698c3 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -33,11 +33,155 @@ enum xpad_cmd_len {
|
|
|
|
/* Values correspond to the actual HID byte value required */
|
|
enum btn_pair_index {
|
|
+ btn_pair_dpad_u_d = 0x01,
|
|
+ btn_pair_dpad_l_r = 0x02,
|
|
+ btn_pair_ls_rs = 0x03,
|
|
+ btn_pair_lb_rb = 0x04,
|
|
+ btn_pair_a_b = 0x05,
|
|
+ btn_pair_x_y = 0x06,
|
|
+ btn_pair_view_menu = 0x07,
|
|
btn_pair_m1_m2 = 0x08,
|
|
+ btn_pair_lt_rt = 0x09,
|
|
};
|
|
|
|
+#define BTN_PAD_A 0x0101000000000000
|
|
+#define BTN_PAD_B 0x0102000000000000
|
|
+#define BTN_PAD_X 0x0103000000000000
|
|
+#define BTN_PAD_Y 0x0104000000000000
|
|
+#define BTN_PAD_LB 0x0105000000000000
|
|
+#define BTN_PAD_RB 0x0106000000000000
|
|
+#define BTN_PAD_LS 0x0107000000000000
|
|
+#define BTN_PAD_RS 0x0108000000000000
|
|
+#define BTN_PAD_DPAD_UP 0x0109000000000000
|
|
+#define BTN_PAD_DPAD_DOWN 0x010A000000000000
|
|
+#define BTN_PAD_DPAD_LEFT 0x010B000000000000
|
|
+#define BTN_PAD_DPAD_RIGHT 0x010C000000000000
|
|
+#define BTN_PAD_LT 0x010D000000000000
|
|
+#define BTN_PAD_RT 0x010E000000000000
|
|
+#define BTN_PAD_VIEW 0x0111000000000000
|
|
+#define BTN_PAD_MENU 0x0112000000000000
|
|
+#define BTN_PAD_XBOX 0x0113000000000000
|
|
+
|
|
#define BTN_KB_M2 0x02008E0000000000
|
|
#define BTN_KB_M1 0x02008F0000000000
|
|
+#define BTN_KB_ESC 0x0200760000000000
|
|
+#define BTN_KB_F1 0x0200500000000000
|
|
+#define BTN_KB_F2 0x0200600000000000
|
|
+#define BTN_KB_F3 0x0200400000000000
|
|
+#define BTN_KB_F4 0x02000C0000000000
|
|
+#define BTN_KB_F5 0x0200030000000000
|
|
+#define BTN_KB_F6 0x02000B0000000000
|
|
+#define BTN_KB_F7 0x0200800000000000
|
|
+#define BTN_KB_F8 0x02000A0000000000
|
|
+#define BTN_KB_F9 0x0200010000000000
|
|
+#define BTN_KB_F10 0x0200090000000000
|
|
+#define BTN_KB_F11 0x0200780000000000
|
|
+#define BTN_KB_F12 0x0200070000000000
|
|
+#define BTN_KB_F14 0x0200180000000000
|
|
+#define BTN_KB_F15 0x0200100000000000
|
|
+#define BTN_KB_BACKTICK 0x02000E0000000000
|
|
+#define BTN_KB_1 0x0200160000000000
|
|
+#define BTN_KB_2 0x02001E0000000000
|
|
+#define BTN_KB_3 0x0200260000000000
|
|
+#define BTN_KB_4 0x0200250000000000
|
|
+#define BTN_KB_5 0x02002E0000000000
|
|
+#define BTN_KB_6 0x0200360000000000
|
|
+#define BTN_KB_7 0x02003D0000000000
|
|
+#define BTN_KB_8 0x02003E0000000000
|
|
+#define BTN_KB_9 0x0200460000000000
|
|
+#define BTN_KB_0 0x0200450000000000
|
|
+#define BTN_KB_HYPHEN 0x02004E0000000000
|
|
+#define BTN_KB_EQUALS 0x0200550000000000
|
|
+#define BTN_KB_BACKSPACE 0x0200660000000000
|
|
+#define BTN_KB_TAB 0x02000D0000000000
|
|
+#define BTN_KB_Q 0x0200150000000000
|
|
+#define BTN_KB_W 0x02001D0000000000
|
|
+#define BTN_KB_E 0x0200240000000000
|
|
+#define BTN_KB_R 0x02002D0000000000
|
|
+#define BTN_KB_T 0x02002C0000000000
|
|
+#define BTN_KB_Y 0x0200350000000000
|
|
+#define BTN_KB_U 0x02003C0000000000
|
|
+#define BTN_KB_O 0x0200440000000000
|
|
+#define BTN_KB_P 0x02004D0000000000
|
|
+#define BTN_KB_LBRACKET 0x0200540000000000
|
|
+#define BTN_KB_RBRACKET 0x02005B0000000000
|
|
+#define BTN_KB_BACKSLASH 0x02005D0000000000
|
|
+#define BTN_KB_CAPS 0x0200580000000000
|
|
+#define BTN_KB_A 0x02001C0000000000
|
|
+#define BTN_KB_S 0x02001B0000000000
|
|
+#define BTN_KB_D 0x0200230000000000
|
|
+#define BTN_KB_F 0x02002B0000000000
|
|
+#define BTN_KB_G 0x0200340000000000
|
|
+#define BTN_KB_H 0x0200330000000000
|
|
+#define BTN_KB_J 0x02003B0000000000
|
|
+#define BTN_KB_K 0x0200420000000000
|
|
+#define BTN_KB_L 0x02004B0000000000
|
|
+#define BTN_KB_SEMI 0x02004C0000000000
|
|
+#define BTN_KB_QUOTE 0x0200520000000000
|
|
+#define BTN_KB_RET 0x02005A0000000000
|
|
+#define BTN_KB_LSHIFT 0x0200880000000000
|
|
+#define BTN_KB_Z 0x02001A0000000000
|
|
+#define BTN_KB_X 0x0200220000000000
|
|
+#define BTN_KB_C 0x0200210000000000
|
|
+#define BTN_KB_V 0x02002A0000000000
|
|
+#define BTN_KB_B 0x0200320000000000
|
|
+#define BTN_KB_N 0x0200310000000000
|
|
+#define BTN_KB_M 0x02003A0000000000
|
|
+#define BTN_KB_COMMA 0x0200410000000000
|
|
+#define BTN_KB_PERIOD 0x0200490000000000
|
|
+#define BTN_KB_RSHIFT 0x0200890000000000
|
|
+#define BTN_KB_LCTL 0x02008C0000000000
|
|
+#define BTN_KB_META 0x0200820000000000
|
|
+#define BTN_KB_LALT 0x02008A0000000000
|
|
+#define BTN_KB_SPACE 0x0200290000000000
|
|
+#define BTN_KB_RALT 0x02008B0000000000
|
|
+#define BTN_KB_MENU 0x0200840000000000
|
|
+#define BTN_KB_RCTL 0x02008D0000000000
|
|
+#define BTN_KB_PRNTSCN 0x0200C30000000000
|
|
+#define BTN_KB_SCRLCK 0x02007E0000000000
|
|
+#define BTN_KB_PAUSE 0x0200910000000000
|
|
+#define BTN_KB_INS 0x0200C20000000000
|
|
+#define BTN_KB_HOME 0x0200940000000000
|
|
+#define BTN_KB_PGUP 0x0200960000000000
|
|
+#define BTN_KB_DEL 0x0200C00000000000
|
|
+#define BTN_KB_END 0x0200950000000000
|
|
+#define BTN_KB_PGDWN 0x0200970000000000
|
|
+#define BTN_KB_UP_ARROW 0x0200980000000000
|
|
+#define BTN_KB_DOWN_ARROW 0x0200990000000000
|
|
+#define BTN_KB_LEFT_ARROW 0x0200910000000000
|
|
+#define BTN_KB_RIGHT_ARROW 0x02009B0000000000
|
|
+
|
|
+#define BTN_NUMPAD_LOCK 0x0200770000000000
|
|
+#define BTN_NUMPAD_FWDSLASH 0x0200900000000000
|
|
+#define BTN_NUMPAD_ASTERISK 0x02007C0000000000
|
|
+#define BTN_NUMPAD_HYPHEN 0x02007B0000000000
|
|
+#define BTN_NUMPAD_0 0x0200700000000000
|
|
+#define BTN_NUMPAD_1 0x0200690000000000
|
|
+#define BTN_NUMPAD_2 0x0200720000000000
|
|
+#define BTN_NUMPAD_3 0x02007A0000000000
|
|
+#define BTN_NUMPAD_4 0x02006B0000000000
|
|
+#define BTN_NUMPAD_5 0x0200730000000000
|
|
+#define BTN_NUMPAD_6 0x0200740000000000
|
|
+#define BTN_NUMPAD_7 0x02006C0000000000
|
|
+#define BTN_NUMPAD_8 0x0200750000000000
|
|
+#define BTN_NUMPAD_9 0x02007D0000000000
|
|
+#define BTN_NUMPAD_PLUS 0x0200790000000000
|
|
+#define BTN_NUMPAD_ENTER 0x0200810000000000
|
|
+#define BTN_NUMPAD_PERIOD 0x0200710000000000
|
|
+
|
|
+#define BTN_MOUSE_LCLICK 0x0300000001000000
|
|
+#define BTN_MOUSE_RCLICK 0x0300000002000000
|
|
+#define BTN_MOUSE_MCLICK 0x0300000003000000
|
|
+#define BTN_MOUSE_WHEEL_UP 0x0300000004000000
|
|
+#define BTN_MOUSE_WHEEL_DOWN 0x0300000005000000
|
|
+
|
|
+#define BTN_MEDIA_SCREENSHOT 0x0500001600000000
|
|
+#define BTN_MEDIA_SHOW_KEYBOARD 0x0500001900000000
|
|
+#define BTN_MEDIA_SHOW_DESKTOP 0x0500001C00000000
|
|
+#define BTN_MEDIA_START_RECORDING 0x0500001E00000000
|
|
+#define BTN_MEDIA_MIC_OFF 0x0500000100000000
|
|
+#define BTN_MEDIA_VOL_DOWN 0x0500000200000000
|
|
+#define BTN_MEDIA_VOL_UP 0x0500000300000000
|
|
|
|
#define ALLY_DEVICE_ATTR_WO(_name, _sysfs_name) \
|
|
struct device_attribute dev_attr_##_name = \
|
|
@@ -47,3 +191,70 @@ enum btn_pair_index {
|
|
#define ALLY_DEVICE_ATTR_RW(_name, _sysfs_name) \
|
|
struct device_attribute dev_attr_##_name = \
|
|
__ATTR(_sysfs_name, 0644, _name##_show, _name##_store)
|
|
+
|
|
+/* button specific macros */
|
|
+#define ALLY_BTN_SHOW(_fname, _btn_name, _secondary) \
|
|
+ static ssize_t _fname##_show(struct device *dev, \
|
|
+ struct device_attribute *attr, char *buf) \
|
|
+ { \
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
|
|
+ struct btn_data *btn; \
|
|
+ const char* name; \
|
|
+ if (!drvdata.gamepad_cfg) \
|
|
+ return -ENODEV; \
|
|
+ btn = &ally_cfg->key_mapping[ally_cfg->mode - 1]._btn_name; \
|
|
+ name = btn_to_name(_secondary ? btn->macro : btn->button); \
|
|
+ return sysfs_emit(buf, "%s\n", name); \
|
|
+ }
|
|
+
|
|
+#define ALLY_BTN_STORE(_fname, _btn_name, _secondary) \
|
|
+ static ssize_t _fname##_store(struct device *dev, \
|
|
+ struct device_attribute *attr, \
|
|
+ const char *buf, size_t count) \
|
|
+ { \
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
|
|
+ struct btn_data *btn; \
|
|
+ u64 code; \
|
|
+ if (!drvdata.gamepad_cfg) \
|
|
+ return -ENODEV; \
|
|
+ btn = &ally_cfg->key_mapping[ally_cfg->mode - 1]._btn_name; \
|
|
+ code = name_to_btn(buf); \
|
|
+ if (_secondary) \
|
|
+ btn->macro = code; \
|
|
+ else \
|
|
+ btn->button = code; \
|
|
+ return count; \
|
|
+ }
|
|
+
|
|
+#define ALLY_BTN_ATTRS_GROUP(_name, _fname) \
|
|
+ static struct attribute *_fname##_attrs[] = { \
|
|
+ &dev_attr_##_fname.attr, \
|
|
+ &dev_attr_##_fname##_macro.attr, \
|
|
+ }; \
|
|
+ static const struct attribute_group _fname##_attr_group = { \
|
|
+ .name = __stringify(_name), \
|
|
+ .attrs = _fname##_attrs, \
|
|
+ }
|
|
+
|
|
+#define _ALLY_BTN_REMAP(_fname, _btn_name) \
|
|
+ ALLY_BTN_SHOW(btn_mapping_##_fname##_remap, _btn_name, false); \
|
|
+ ALLY_BTN_STORE(btn_mapping_##_fname##_remap, _btn_name, false); \
|
|
+ ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname##_remap, remap);
|
|
+
|
|
+#define _ALLY_BTN_MACRO(_fname, _btn_name) \
|
|
+ ALLY_BTN_SHOW(btn_mapping_##_fname##_macro, _btn_name, true); \
|
|
+ ALLY_BTN_STORE(btn_mapping_##_fname##_macro, _btn_name, true); \
|
|
+ ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname##_macro, macro_remap);
|
|
+
|
|
+#define ALLY_BTN_MAPPING(_fname, _btn_name) \
|
|
+ _ALLY_BTN_REMAP(_fname, _btn_name) \
|
|
+ _ALLY_BTN_MACRO(_fname, _btn_name) \
|
|
+ static struct attribute *_fname##_attrs[] = { \
|
|
+ &dev_attr_btn_mapping_##_fname##_remap.attr, \
|
|
+ &dev_attr_btn_mapping_##_fname##_macro.attr, \
|
|
+ NULL, \
|
|
+ }; \
|
|
+ static const struct attribute_group btn_mapping_##_fname##_attr_group = { \
|
|
+ .name = __stringify(btn_##_fname), \
|
|
+ .attrs = _fname##_attrs, \
|
|
+ }
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 9bd5cccfdb79320029875979dede71904f068cc1 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Fri, 25 Oct 2024 08:56:54 +0200
|
|
Subject: [PATCH 11/28] hid-asus-ally: add gamepad mode selection
|
|
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 73 +++++++++++++++++++++++++++++++++++++
|
|
drivers/hid/hid-asus-ally.h | 2 +
|
|
2 files changed, 75 insertions(+)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 884cb688197e..08e953f6a3c5 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -647,9 +647,82 @@ static ssize_t btn_mapping_reset_store(struct device *dev, struct device_attribu
|
|
}
|
|
ALLY_DEVICE_ATTR_WO(btn_mapping_reset, reset_btn_mapping);
|
|
|
|
+/* GAMEPAD MODE */
|
|
+static ssize_t _gamepad_set_mode(struct hid_device *hdev, struct ally_gamepad_cfg *ally_cfg,
|
|
+ int val)
|
|
+{
|
|
+ u8 *hidbuf;
|
|
+ int ret;
|
|
+
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
|
|
+ hidbuf[2] = xpad_cmd_set_mode;
|
|
+ hidbuf[3] = xpad_cmd_len_mode;
|
|
+ hidbuf[4] = val;
|
|
+
|
|
+ ret = ally_gamepad_check_ready(hdev);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+ ret = _gamepad_apply_all(hdev, ally_cfg);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+report_fail:
|
|
+ kfree(hidbuf);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t gamepad_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+
|
|
+ if (!drvdata.gamepad_cfg)
|
|
+ return -ENODEV;
|
|
+
|
|
+ return sysfs_emit(buf, "%d\n", ally_cfg->mode);
|
|
+}
|
|
+
|
|
+static ssize_t gamepad_mode_store(struct device *dev, struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct hid_device *hdev = to_hid_device(dev);
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+ int ret, val;
|
|
+
|
|
+ if (!drvdata.gamepad_cfg)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = kstrtoint(buf, 0, &val);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (val < xpad_mode_game || val > xpad_mode_mouse)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ally_cfg->mode = val;
|
|
+
|
|
+ ret = _gamepad_set_mode(hdev, ally_cfg, val);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+DEVICE_ATTR_RW(gamepad_mode);
|
|
+
|
|
/* ROOT LEVEL ATTRS *******************************************************************************/
|
|
static struct attribute *gamepad_device_attrs[] = {
|
|
&dev_attr_btn_mapping_reset.attr,
|
|
+ &dev_attr_gamepad_mode.attr,
|
|
&dev_attr_gamepad_apply_all.attr,
|
|
NULL
|
|
};
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index f985cbd698c3..f7e21be50d8e 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -20,6 +20,7 @@ enum xpad_mode {
|
|
|
|
/* the xpad_cmd determines which feature is set or queried */
|
|
enum xpad_cmd {
|
|
+ xpad_cmd_set_mode = 0x01,
|
|
xpad_cmd_set_mapping = 0x02,
|
|
xpad_cmd_set_leds = 0x08,
|
|
xpad_cmd_check_ready = 0x0A,
|
|
@@ -27,6 +28,7 @@ enum xpad_cmd {
|
|
|
|
/* the xpad_cmd determines which feature is set or queried */
|
|
enum xpad_cmd_len {
|
|
+ xpad_cmd_len_mode = 0x01,
|
|
xpad_cmd_len_mapping = 0x2c,
|
|
xpad_cmd_len_leds = 0x0C,
|
|
};
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 7864114a616a7e485fd12afe6a80ebbcfd756215 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sat, 5 Oct 2024 15:40:09 +1300
|
|
Subject: [PATCH 12/28] hid-asus-ally: Turbo settings for buttons
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 72 +++++++++++++++++++++++++++++--------
|
|
drivers/hid/hid-asus-ally.h | 50 ++++++++++++++++++++++++++
|
|
2 files changed, 108 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 08e953f6a3c5..53c2b36c14fb 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -236,6 +236,7 @@ static const char* btn_to_name(u64 key)
|
|
struct btn_data {
|
|
u64 button;
|
|
u64 macro;
|
|
+ bool turbo;
|
|
};
|
|
|
|
struct btn_mapping {
|
|
@@ -530,6 +531,46 @@ static int _gamepad_apply_btn_pair(struct hid_device *hdev, struct ally_gamepad_
|
|
return ret;
|
|
}
|
|
|
|
+static int _gamepad_apply_turbo(struct hid_device *hdev, struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ struct btn_mapping *map = &ally_cfg->key_mapping[ally_cfg->mode - 1];
|
|
+ u8 *hidbuf;
|
|
+ int ret;
|
|
+
|
|
+ /* set turbo */
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
|
|
+ hidbuf[2] = xpad_cmd_set_turbo;
|
|
+ hidbuf[3] = xpad_cmd_len_turbo;
|
|
+
|
|
+ hidbuf[4] = map->dpad_up.turbo;
|
|
+ hidbuf[6] = map->dpad_down.turbo;
|
|
+ hidbuf[8] = map->dpad_left.turbo;
|
|
+ hidbuf[10] = map->dpad_right.turbo;
|
|
+
|
|
+ hidbuf[12] = map->btn_ls.turbo;
|
|
+ hidbuf[14] = map->btn_rs.turbo;
|
|
+ hidbuf[16] = map->btn_lb.turbo;
|
|
+ hidbuf[18] = map->btn_rb.turbo;
|
|
+
|
|
+ hidbuf[20] = map->btn_a.turbo;
|
|
+ hidbuf[22] = map->btn_b.turbo;
|
|
+ hidbuf[24] = map->btn_x.turbo;
|
|
+ hidbuf[26] = map->btn_y.turbo;
|
|
+
|
|
+ hidbuf[28] = map->btn_lt.turbo;
|
|
+ hidbuf[30] = map->btn_rt.turbo;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+
|
|
+ kfree(hidbuf);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static ssize_t _gamepad_apply_all(struct hid_device *hdev, struct ally_gamepad_cfg *ally_cfg)
|
|
{
|
|
int ret;
|
|
@@ -559,6 +600,9 @@ static ssize_t _gamepad_apply_all(struct hid_device *hdev, struct ally_gamepad_c
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = _gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_lt_rt);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_turbo(hdev, ally_cfg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
@@ -586,22 +630,22 @@ ALLY_DEVICE_ATTR_WO(gamepad_apply_all, apply_all);
|
|
/* button map attributes, regular and macro*/
|
|
ALLY_BTN_MAPPING(m1, btn_m1);
|
|
ALLY_BTN_MAPPING(m2, btn_m2);
|
|
-ALLY_BTN_MAPPING(a, btn_a);
|
|
-ALLY_BTN_MAPPING(b, btn_b);
|
|
-ALLY_BTN_MAPPING(x, btn_x);
|
|
-ALLY_BTN_MAPPING(y, btn_y);
|
|
-ALLY_BTN_MAPPING(lb, btn_lb);
|
|
-ALLY_BTN_MAPPING(rb, btn_rb);
|
|
-ALLY_BTN_MAPPING(ls, btn_ls);
|
|
-ALLY_BTN_MAPPING(rs, btn_rs);
|
|
-ALLY_BTN_MAPPING(lt, btn_lt);
|
|
-ALLY_BTN_MAPPING(rt, btn_rt);
|
|
-ALLY_BTN_MAPPING(dpad_u, dpad_up);
|
|
-ALLY_BTN_MAPPING(dpad_d, dpad_down);
|
|
-ALLY_BTN_MAPPING(dpad_l, dpad_left);
|
|
-ALLY_BTN_MAPPING(dpad_r, dpad_right);
|
|
ALLY_BTN_MAPPING(view, btn_view);
|
|
ALLY_BTN_MAPPING(menu, btn_menu);
|
|
+ALLY_TURBO_BTN_MAPPING(a, btn_a);
|
|
+ALLY_TURBO_BTN_MAPPING(b, btn_b);
|
|
+ALLY_TURBO_BTN_MAPPING(x, btn_x);
|
|
+ALLY_TURBO_BTN_MAPPING(y, btn_y);
|
|
+ALLY_TURBO_BTN_MAPPING(lb, btn_lb);
|
|
+ALLY_TURBO_BTN_MAPPING(rb, btn_rb);
|
|
+ALLY_TURBO_BTN_MAPPING(ls, btn_ls);
|
|
+ALLY_TURBO_BTN_MAPPING(rs, btn_rs);
|
|
+ALLY_TURBO_BTN_MAPPING(lt, btn_lt);
|
|
+ALLY_TURBO_BTN_MAPPING(rt, btn_rt);
|
|
+ALLY_TURBO_BTN_MAPPING(dpad_u, dpad_up);
|
|
+ALLY_TURBO_BTN_MAPPING(dpad_d, dpad_down);
|
|
+ALLY_TURBO_BTN_MAPPING(dpad_l, dpad_left);
|
|
+ALLY_TURBO_BTN_MAPPING(dpad_r, dpad_right);
|
|
|
|
static void _gamepad_set_xpad_default(struct ally_gamepad_cfg *ally_cfg)
|
|
{
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index f7e21be50d8e..63a3b5caa71c 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -24,6 +24,7 @@ enum xpad_cmd {
|
|
xpad_cmd_set_mapping = 0x02,
|
|
xpad_cmd_set_leds = 0x08,
|
|
xpad_cmd_check_ready = 0x0A,
|
|
+ xpad_cmd_set_turbo = 0x0F,
|
|
};
|
|
|
|
/* the xpad_cmd determines which feature is set or queried */
|
|
@@ -31,6 +32,7 @@ enum xpad_cmd_len {
|
|
xpad_cmd_len_mode = 0x01,
|
|
xpad_cmd_len_mapping = 0x2c,
|
|
xpad_cmd_len_leds = 0x0C,
|
|
+ xpad_cmd_len_turbo = 0x20,
|
|
};
|
|
|
|
/* Values correspond to the actual HID byte value required */
|
|
@@ -228,6 +230,37 @@ enum btn_pair_index {
|
|
return count; \
|
|
}
|
|
|
|
+#define ALLY_TURBO_SHOW(_fname, _btn_name) \
|
|
+ static ssize_t _fname##_show(struct device *dev, \
|
|
+ struct device_attribute *attr, char *buf) \
|
|
+ { \
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
|
|
+ struct btn_data *btn; \
|
|
+ if (!drvdata.gamepad_cfg) \
|
|
+ return -ENODEV; \
|
|
+ btn = &ally_cfg->key_mapping[ally_cfg->mode - 1]._btn_name; \
|
|
+ return sysfs_emit(buf, "%d\n", btn->turbo); \
|
|
+ }
|
|
+
|
|
+#define ALLY_TURBO_STORE(_fname, _btn_name) \
|
|
+ static ssize_t _fname##_store(struct device *dev, \
|
|
+ struct device_attribute *attr, \
|
|
+ const char *buf, size_t count) \
|
|
+ { \
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
|
|
+ struct btn_data *btn; \
|
|
+ bool turbo; \
|
|
+ int ret; \
|
|
+ if (!drvdata.gamepad_cfg) \
|
|
+ return -ENODEV; \
|
|
+ btn = &ally_cfg->key_mapping[ally_cfg->mode - 1]._btn_name; \
|
|
+ ret = kstrtobool(buf, &turbo); \
|
|
+ if (ret) \
|
|
+ return ret; \
|
|
+ btn->turbo = turbo; \
|
|
+ return count; \
|
|
+ }
|
|
+
|
|
#define ALLY_BTN_ATTRS_GROUP(_name, _fname) \
|
|
static struct attribute *_fname##_attrs[] = { \
|
|
&dev_attr_##_fname.attr, \
|
|
@@ -260,3 +293,20 @@ enum btn_pair_index {
|
|
.name = __stringify(btn_##_fname), \
|
|
.attrs = _fname##_attrs, \
|
|
}
|
|
+
|
|
+#define ALLY_TURBO_BTN_MAPPING(_fname, _btn_name) \
|
|
+ _ALLY_BTN_REMAP(_fname, _btn_name) \
|
|
+ _ALLY_BTN_MACRO(_fname, _btn_name) \
|
|
+ ALLY_TURBO_SHOW(btn_mapping_##_fname##_turbo, _btn_name); \
|
|
+ ALLY_TURBO_STORE(btn_mapping_##_fname##_turbo, _btn_name); \
|
|
+ ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname##_turbo, turbo); \
|
|
+ static struct attribute *_fname##_turbo_attrs[] = { \
|
|
+ &dev_attr_btn_mapping_##_fname##_remap.attr, \
|
|
+ &dev_attr_btn_mapping_##_fname##_macro.attr, \
|
|
+ &dev_attr_btn_mapping_##_fname##_turbo.attr, \
|
|
+ NULL, \
|
|
+ }; \
|
|
+ static const struct attribute_group btn_mapping_##_fname##_attr_group = { \
|
|
+ .name = __stringify(btn_##_fname), \
|
|
+ .attrs = _fname##_turbo_attrs, \
|
|
+ }
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From f19752b0af90acea553188b1baa9f18dabc684c2 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sat, 5 Oct 2024 20:46:00 +1300
|
|
Subject: [PATCH 13/28] hid-asus-ally: add vibration intensity settings
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 93 +++++++++++++++++++++++++++++++++++++
|
|
drivers/hid/hid-asus-ally.h | 6 +++
|
|
2 files changed, 99 insertions(+)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 53c2b36c14fb..8b40b806631c 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -270,6 +270,11 @@ struct ally_gamepad_cfg {
|
|
* index: [mode]
|
|
*/
|
|
struct btn_mapping key_mapping[xpad_mode_mouse];
|
|
+ /*
|
|
+ * index: left, right
|
|
+ * max: 64
|
|
+ */
|
|
+ u8 vibration_intensity[2];
|
|
};
|
|
|
|
/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
@@ -441,6 +446,89 @@ static int ally_gamepad_check_ready(struct hid_device *hdev)
|
|
return ret;
|
|
}
|
|
|
|
+/* VIBRATION INTENSITY ****************************************************************************/
|
|
+static ssize_t gamepad_vibration_intensity_index_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ return sysfs_emit(buf, "left right\n");
|
|
+}
|
|
+
|
|
+ALLY_DEVICE_ATTR_RO(gamepad_vibration_intensity_index, vibration_intensity_index);
|
|
+
|
|
+static ssize_t _gamepad_apply_intensity(struct hid_device *hdev,
|
|
+ struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ u8 *hidbuf;
|
|
+ int ret;
|
|
+
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
|
|
+ hidbuf[2] = xpad_cmd_set_vibe_intensity;
|
|
+ hidbuf[3] = xpad_cmd_len_vibe_intensity;
|
|
+ hidbuf[4] = ally_cfg->vibration_intensity[0];
|
|
+ hidbuf[5] = ally_cfg->vibration_intensity[1];
|
|
+
|
|
+ ret = ally_gamepad_check_ready(hdev);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+report_fail:
|
|
+ kfree(hidbuf);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t gamepad_vibration_intensity_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+
|
|
+ if (!drvdata.gamepad_cfg)
|
|
+ return -ENODEV;
|
|
+
|
|
+ return sysfs_emit(
|
|
+ buf, "%d %d\n",
|
|
+ ally_cfg->vibration_intensity[0],
|
|
+ ally_cfg->vibration_intensity[1]);
|
|
+}
|
|
+
|
|
+static ssize_t gamepad_vibration_intensity_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ struct hid_device *hdev = to_hid_device(dev);
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+ u32 left, right;
|
|
+ int ret;
|
|
+
|
|
+ if (!drvdata.gamepad_cfg)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (sscanf(buf, "%d %d", &left, &right) != 2)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (left > 64 || right > 64)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ally_cfg->vibration_intensity[0] = left;
|
|
+ ally_cfg->vibration_intensity[1] = right;
|
|
+
|
|
+ ret = _gamepad_apply_intensity(hdev, ally_cfg);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+ALLY_DEVICE_ATTR_RW(gamepad_vibration_intensity, vibration_intensity);
|
|
+
|
|
/* A HID packet conatins mappings for two buttons: btn1, btn1_macro, btn2, btn2_macro */
|
|
static void _btn_pair_to_hid_pkt(struct ally_gamepad_cfg *ally_cfg,
|
|
enum btn_pair_index pair,
|
|
@@ -768,6 +856,8 @@ static struct attribute *gamepad_device_attrs[] = {
|
|
&dev_attr_btn_mapping_reset.attr,
|
|
&dev_attr_gamepad_mode.attr,
|
|
&dev_attr_gamepad_apply_all.attr,
|
|
+ &dev_attr_gamepad_vibration_intensity.attr,
|
|
+ &dev_attr_gamepad_vibration_intensity_index.attr,
|
|
NULL
|
|
};
|
|
|
|
@@ -840,6 +930,9 @@ static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
ally_cfg->key_mapping[ally_cfg->mode - 1].btn_m2.button = BTN_KB_M2;
|
|
_gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_m1_m2);
|
|
|
|
+ ally_cfg->vibration_intensity[0] = 0x64;
|
|
+ ally_cfg->vibration_intensity[1] = 0x64;
|
|
+
|
|
drvdata.gamepad_cfg = ally_cfg; // Must asign before attr group setup
|
|
if (sysfs_create_groups(&hdev->dev.kobj, gamepad_device_attr_groups)) {
|
|
err = -ENODEV;
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index 63a3b5caa71c..6ac79ad3c5f2 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -22,6 +22,7 @@ enum xpad_mode {
|
|
enum xpad_cmd {
|
|
xpad_cmd_set_mode = 0x01,
|
|
xpad_cmd_set_mapping = 0x02,
|
|
+ xpad_cmd_set_vibe_intensity = 0x06,
|
|
xpad_cmd_set_leds = 0x08,
|
|
xpad_cmd_check_ready = 0x0A,
|
|
xpad_cmd_set_turbo = 0x0F,
|
|
@@ -31,6 +32,7 @@ enum xpad_cmd {
|
|
enum xpad_cmd_len {
|
|
xpad_cmd_len_mode = 0x01,
|
|
xpad_cmd_len_mapping = 0x2c,
|
|
+ xpad_cmd_len_vibe_intensity = 0x02,
|
|
xpad_cmd_len_leds = 0x0C,
|
|
xpad_cmd_len_turbo = 0x20,
|
|
};
|
|
@@ -196,6 +198,10 @@ enum btn_pair_index {
|
|
struct device_attribute dev_attr_##_name = \
|
|
__ATTR(_sysfs_name, 0644, _name##_show, _name##_store)
|
|
|
|
+#define ALLY_DEVICE_ATTR_RO(_name, _sysfs_name) \
|
|
+ struct device_attribute dev_attr_##_name = \
|
|
+ __ATTR(_sysfs_name, 0444, _name##_show, NULL)
|
|
+
|
|
/* button specific macros */
|
|
#define ALLY_BTN_SHOW(_fname, _btn_name, _secondary) \
|
|
static ssize_t _fname##_show(struct device *dev, \
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From e59c4657466b382c250568f87ec742bfefaa25bf Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sat, 5 Oct 2024 21:32:41 +1300
|
|
Subject: [PATCH 14/28] hid-asus-ally: add JS deadzones
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 84 +++++++++++++++++++++++++++++++++++++
|
|
drivers/hid/hid-asus-ally.h | 39 +++++++++++++++++
|
|
2 files changed, 123 insertions(+)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 8b40b806631c..64f4b466f0bb 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -260,6 +260,11 @@ struct btn_mapping {
|
|
struct btn_data btn_m2;
|
|
};
|
|
|
|
+struct deadzone {
|
|
+ u8 inner;
|
|
+ u8 outer;
|
|
+};
|
|
+
|
|
/* ROG Ally has many settings related to the gamepad, all using the same n-key endpoint */
|
|
struct ally_gamepad_cfg {
|
|
struct hid_device *hdev;
|
|
@@ -275,6 +280,10 @@ struct ally_gamepad_cfg {
|
|
* max: 64
|
|
*/
|
|
u8 vibration_intensity[2];
|
|
+
|
|
+ /* deadzones */
|
|
+ struct deadzone ls_dz; // left stick
|
|
+ struct deadzone rs_dz; // right stick
|
|
};
|
|
|
|
/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
@@ -529,6 +538,75 @@ static ssize_t gamepad_vibration_intensity_store(struct device *dev,
|
|
|
|
ALLY_DEVICE_ATTR_RW(gamepad_vibration_intensity, vibration_intensity);
|
|
|
|
+/* ANALOGUE DEADZONES *****************************************************************************/
|
|
+static ssize_t _gamepad_apply_deadzones(struct hid_device *hdev,
|
|
+ struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ u8 *hidbuf;
|
|
+ int ret;
|
|
+
|
|
+ ret = ally_gamepad_check_ready(hdev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
|
|
+ hidbuf[2] = xpad_cmd_set_js_dz;
|
|
+ hidbuf[3] = xpad_cmd_len_deadzone;
|
|
+ hidbuf[4] = ally_cfg->ls_dz.inner;
|
|
+ hidbuf[5] = ally_cfg->ls_dz.outer;
|
|
+ hidbuf[6] = ally_cfg->rs_dz.inner;
|
|
+ hidbuf[7] = ally_cfg->rs_dz.outer;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+
|
|
+ kfree(hidbuf);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void _gamepad_set_deadzones_default(struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ ally_cfg->ls_dz.inner = 0x00;
|
|
+ ally_cfg->ls_dz.outer = 0x64;
|
|
+ ally_cfg->rs_dz.inner = 0x00;
|
|
+ ally_cfg->rs_dz.outer = 0x64;
|
|
+}
|
|
+
|
|
+static ssize_t axis_xyz_deadzone_index_show(struct device *dev, struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return sysfs_emit(buf, "inner outer\n");
|
|
+}
|
|
+
|
|
+ALLY_DEVICE_ATTR_RO(axis_xyz_deadzone_index, deadzone_index);
|
|
+
|
|
+ALLY_DEADZONES(axis_xy_left, ls_dz);
|
|
+ALLY_DEADZONES(axis_xy_right, rs_dz);
|
|
+
|
|
+static struct attribute *axis_xy_left_attrs[] = {
|
|
+ &dev_attr_axis_xy_left_deadzone.attr,
|
|
+ &dev_attr_axis_xyz_deadzone_index.attr,
|
|
+ NULL
|
|
+};
|
|
+static const struct attribute_group axis_xy_left_attr_group = {
|
|
+ .name = "axis_xy_left",
|
|
+ .attrs = axis_xy_left_attrs,
|
|
+};
|
|
+
|
|
+static struct attribute *axis_xy_right_attrs[] = {
|
|
+ &dev_attr_axis_xy_right_deadzone.attr,
|
|
+ &dev_attr_axis_xyz_deadzone_index.attr,
|
|
+ NULL
|
|
+};
|
|
+static const struct attribute_group axis_xy_right_attr_group = {
|
|
+ .name = "axis_xy_right",
|
|
+ .attrs = axis_xy_right_attrs,
|
|
+};
|
|
+
|
|
/* A HID packet conatins mappings for two buttons: btn1, btn1_macro, btn2, btn2_macro */
|
|
static void _btn_pair_to_hid_pkt(struct ally_gamepad_cfg *ally_cfg,
|
|
enum btn_pair_index pair,
|
|
@@ -691,6 +769,9 @@ static ssize_t _gamepad_apply_all(struct hid_device *hdev, struct ally_gamepad_c
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = _gamepad_apply_turbo(hdev, ally_cfg);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_deadzones(hdev, ally_cfg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
@@ -867,6 +948,8 @@ static const struct attribute_group ally_controller_attr_group = {
|
|
|
|
static const struct attribute_group *gamepad_device_attr_groups[] = {
|
|
&ally_controller_attr_group,
|
|
+ &axis_xy_left_attr_group,
|
|
+ &axis_xy_right_attr_group,
|
|
&btn_mapping_m1_attr_group,
|
|
&btn_mapping_m2_attr_group,
|
|
&btn_mapping_a_attr_group,
|
|
@@ -932,6 +1015,7 @@ static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
|
|
ally_cfg->vibration_intensity[0] = 0x64;
|
|
ally_cfg->vibration_intensity[1] = 0x64;
|
|
+ _gamepad_set_deadzones_default(ally_cfg);
|
|
|
|
drvdata.gamepad_cfg = ally_cfg; // Must asign before attr group setup
|
|
if (sysfs_create_groups(&hdev->dev.kobj, gamepad_device_attr_groups)) {
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index 6ac79ad3c5f2..3dc14a5226f3 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -22,6 +22,7 @@ enum xpad_mode {
|
|
enum xpad_cmd {
|
|
xpad_cmd_set_mode = 0x01,
|
|
xpad_cmd_set_mapping = 0x02,
|
|
+ xpad_cmd_set_js_dz = 0x04, /* deadzones */
|
|
xpad_cmd_set_vibe_intensity = 0x06,
|
|
xpad_cmd_set_leds = 0x08,
|
|
xpad_cmd_check_ready = 0x0A,
|
|
@@ -32,6 +33,7 @@ enum xpad_cmd {
|
|
enum xpad_cmd_len {
|
|
xpad_cmd_len_mode = 0x01,
|
|
xpad_cmd_len_mapping = 0x2c,
|
|
+ xpad_cmd_len_deadzone = 0x04,
|
|
xpad_cmd_len_vibe_intensity = 0x02,
|
|
xpad_cmd_len_leds = 0x0C,
|
|
xpad_cmd_len_turbo = 0x20,
|
|
@@ -267,6 +269,43 @@ enum btn_pair_index {
|
|
return count; \
|
|
}
|
|
|
|
+#define ALLY_DEADZONE_SHOW(_fname, _axis_name) \
|
|
+ static ssize_t _fname##_show(struct device *dev, \
|
|
+ struct device_attribute *attr, char *buf) \
|
|
+ { \
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
|
|
+ struct deadzone *dz; \
|
|
+ if (!drvdata.gamepad_cfg) \
|
|
+ return -ENODEV; \
|
|
+ dz = &ally_cfg->_axis_name; \
|
|
+ return sysfs_emit(buf, "%d %d\n", dz->inner, dz->outer); \
|
|
+ }
|
|
+
|
|
+#define ALLY_DEADZONE_STORE(_fname, _axis_name) \
|
|
+ static ssize_t _fname##_store(struct device *dev, \
|
|
+ struct device_attribute *attr, \
|
|
+ const char *buf, size_t count) \
|
|
+ { \
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
|
|
+ struct hid_device *hdev = to_hid_device(dev); \
|
|
+ u32 inner, outer; \
|
|
+ if (!drvdata.gamepad_cfg) \
|
|
+ return -ENODEV; \
|
|
+ if (sscanf(buf, "%d %d", &inner, &outer) != 2) \
|
|
+ return -EINVAL; \
|
|
+ if (inner > 64 || outer > 64 || inner > outer) \
|
|
+ return -EINVAL; \
|
|
+ ally_cfg->_axis_name.inner = inner; \
|
|
+ ally_cfg->_axis_name.outer = outer; \
|
|
+ _gamepad_apply_deadzones(hdev, ally_cfg); \
|
|
+ return count; \
|
|
+ }
|
|
+
|
|
+#define ALLY_DEADZONES(_fname, _mname) \
|
|
+ ALLY_DEADZONE_SHOW(_fname##_deadzone, _mname); \
|
|
+ ALLY_DEADZONE_STORE(_fname##_deadzone, _mname); \
|
|
+ ALLY_DEVICE_ATTR_RW(_fname##_deadzone, deadzone)
|
|
+
|
|
#define ALLY_BTN_ATTRS_GROUP(_name, _fname) \
|
|
static struct attribute *_fname##_attrs[] = { \
|
|
&dev_attr_##_fname.attr, \
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 2a5cd6d77dcc471ac4792100ef677c2beba316f0 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sat, 5 Oct 2024 21:37:27 +1300
|
|
Subject: [PATCH 15/28] hid-asus-ally: add trigger deadzones
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 43 +++++++++++++++++++++++++++++++++++++
|
|
drivers/hid/hid-asus-ally.h | 1 +
|
|
2 files changed, 44 insertions(+)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 64f4b466f0bb..4a3e7cea1c5e 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -284,6 +284,8 @@ struct ally_gamepad_cfg {
|
|
/* deadzones */
|
|
struct deadzone ls_dz; // left stick
|
|
struct deadzone rs_dz; // right stick
|
|
+ struct deadzone lt_dz; // left trigger
|
|
+ struct deadzone rt_dz; // right trigger
|
|
};
|
|
|
|
/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
@@ -563,7 +565,20 @@ static ssize_t _gamepad_apply_deadzones(struct hid_device *hdev,
|
|
hidbuf[7] = ally_cfg->rs_dz.outer;
|
|
|
|
ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ goto end;
|
|
+
|
|
+ hidbuf[2] = xpad_cmd_set_tr_dz;
|
|
+ hidbuf[4] = ally_cfg->lt_dz.inner;
|
|
+ hidbuf[5] = ally_cfg->lt_dz.outer;
|
|
+ hidbuf[6] = ally_cfg->rt_dz.inner;
|
|
+ hidbuf[7] = ally_cfg->rt_dz.outer;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ goto end;
|
|
|
|
+end:
|
|
kfree(hidbuf);
|
|
return ret;
|
|
}
|
|
@@ -574,6 +589,10 @@ static void _gamepad_set_deadzones_default(struct ally_gamepad_cfg *ally_cfg)
|
|
ally_cfg->ls_dz.outer = 0x64;
|
|
ally_cfg->rs_dz.inner = 0x00;
|
|
ally_cfg->rs_dz.outer = 0x64;
|
|
+ ally_cfg->lt_dz.inner = 0x00;
|
|
+ ally_cfg->lt_dz.outer = 0x64;
|
|
+ ally_cfg->rt_dz.inner = 0x00;
|
|
+ ally_cfg->rt_dz.outer = 0x64;
|
|
}
|
|
|
|
static ssize_t axis_xyz_deadzone_index_show(struct device *dev, struct device_attribute *attr,
|
|
@@ -586,6 +605,8 @@ ALLY_DEVICE_ATTR_RO(axis_xyz_deadzone_index, deadzone_index);
|
|
|
|
ALLY_DEADZONES(axis_xy_left, ls_dz);
|
|
ALLY_DEADZONES(axis_xy_right, rs_dz);
|
|
+ALLY_DEADZONES(axis_z_left, lt_dz);
|
|
+ALLY_DEADZONES(axis_z_right, rt_dz);
|
|
|
|
static struct attribute *axis_xy_left_attrs[] = {
|
|
&dev_attr_axis_xy_left_deadzone.attr,
|
|
@@ -607,6 +628,26 @@ static const struct attribute_group axis_xy_right_attr_group = {
|
|
.attrs = axis_xy_right_attrs,
|
|
};
|
|
|
|
+static struct attribute *axis_z_left_attrs[] = {
|
|
+ &dev_attr_axis_z_left_deadzone.attr,
|
|
+ &dev_attr_axis_xyz_deadzone_index.attr,
|
|
+ NULL,
|
|
+};
|
|
+static const struct attribute_group axis_z_left_attr_group = {
|
|
+ .name = "axis_z_left",
|
|
+ .attrs = axis_z_left_attrs,
|
|
+};
|
|
+
|
|
+static struct attribute *axis_z_right_attrs[] = {
|
|
+ &dev_attr_axis_z_right_deadzone.attr,
|
|
+ &dev_attr_axis_xyz_deadzone_index.attr,
|
|
+ NULL,
|
|
+};
|
|
+static const struct attribute_group axis_z_right_attr_group = {
|
|
+ .name = "axis_z_right",
|
|
+ .attrs = axis_z_right_attrs,
|
|
+};
|
|
+
|
|
/* A HID packet conatins mappings for two buttons: btn1, btn1_macro, btn2, btn2_macro */
|
|
static void _btn_pair_to_hid_pkt(struct ally_gamepad_cfg *ally_cfg,
|
|
enum btn_pair_index pair,
|
|
@@ -950,6 +991,8 @@ static const struct attribute_group *gamepad_device_attr_groups[] = {
|
|
&ally_controller_attr_group,
|
|
&axis_xy_left_attr_group,
|
|
&axis_xy_right_attr_group,
|
|
+ &axis_z_left_attr_group,
|
|
+ &axis_z_right_attr_group,
|
|
&btn_mapping_m1_attr_group,
|
|
&btn_mapping_m2_attr_group,
|
|
&btn_mapping_a_attr_group,
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index 3dc14a5226f3..32ed5caa3759 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -23,6 +23,7 @@ enum xpad_cmd {
|
|
xpad_cmd_set_mode = 0x01,
|
|
xpad_cmd_set_mapping = 0x02,
|
|
xpad_cmd_set_js_dz = 0x04, /* deadzones */
|
|
+ xpad_cmd_set_tr_dz = 0x05, /* deadzones */
|
|
xpad_cmd_set_vibe_intensity = 0x06,
|
|
xpad_cmd_set_leds = 0x08,
|
|
xpad_cmd_check_ready = 0x0A,
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 8a42534a96ece50ab358fd5a15375aad1a89b39f Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sun, 6 Oct 2024 19:49:24 +1300
|
|
Subject: [PATCH 16/28] hid-asus-ally: add anti-deadzones
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 110 ++++++++++++++++++++++++++++++++++++
|
|
drivers/hid/hid-asus-ally.h | 2 +
|
|
2 files changed, 112 insertions(+)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index 4a3e7cea1c5e..ff1c2cf61e66 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -286,6 +286,9 @@ struct ally_gamepad_cfg {
|
|
struct deadzone rs_dz; // right stick
|
|
struct deadzone lt_dz; // left trigger
|
|
struct deadzone rt_dz; // right trigger
|
|
+ /* anti-deadzones */
|
|
+ u8 ls_adz; // left stick
|
|
+ u8 rs_adz; // right stick
|
|
};
|
|
|
|
/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
@@ -608,7 +611,109 @@ ALLY_DEADZONES(axis_xy_right, rs_dz);
|
|
ALLY_DEADZONES(axis_z_left, lt_dz);
|
|
ALLY_DEADZONES(axis_z_right, rt_dz);
|
|
|
|
+/* ANTI-DEADZONES *********************************************************************************/
|
|
+static ssize_t _gamepad_apply_js_ADZ(struct hid_device *hdev,
|
|
+ struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ u8 *hidbuf;
|
|
+ int ret;
|
|
+
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
|
|
+ hidbuf[2] = xpad_cmd_set_adz;
|
|
+ hidbuf[3] = xpad_cmd_len_adz;
|
|
+ hidbuf[4] = ally_cfg->ls_adz;
|
|
+ hidbuf[5] = ally_cfg->rs_adz;
|
|
+
|
|
+ ret = ally_gamepad_check_ready(hdev);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+report_fail:
|
|
+ kfree(hidbuf);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void _gamepad_set_anti_deadzones_default(struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ ally_cfg->ls_adz = 0x00;
|
|
+ ally_cfg->rs_adz = 0x00;
|
|
+}
|
|
+
|
|
+static ssize_t _gamepad_js_ADZ_store(struct device *dev, const char *buf, u8 *adz)
|
|
+{
|
|
+ int ret, val;
|
|
+
|
|
+ ret = kstrtoint(buf, 0, &val);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (val < 0 || val > 32)
|
|
+ return -EINVAL;
|
|
+
|
|
+ *adz = val;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t axis_xy_left_anti_deadzone_show(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+
|
|
+ return sysfs_emit(buf, "%d\n", ally_cfg->ls_adz);
|
|
+}
|
|
+
|
|
+static ssize_t axis_xy_left_anti_deadzone_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+ int ret;
|
|
+
|
|
+ ret = _gamepad_js_ADZ_store(dev, buf, &ally_cfg->ls_adz);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+ALLY_DEVICE_ATTR_RW(axis_xy_left_anti_deadzone, anti_deadzone);
|
|
+
|
|
+static ssize_t axis_xy_right_anti_deadzone_show(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+
|
|
+ return sysfs_emit(buf, "%d\n", ally_cfg->rs_adz);
|
|
+}
|
|
+
|
|
+static ssize_t axis_xy_right_anti_deadzone_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+ int ret;
|
|
+
|
|
+ ret = _gamepad_js_ADZ_store(dev, buf, &ally_cfg->rs_adz);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+ALLY_DEVICE_ATTR_RW(axis_xy_right_anti_deadzone, anti_deadzone);
|
|
+
|
|
static struct attribute *axis_xy_left_attrs[] = {
|
|
+ &dev_attr_axis_xy_left_anti_deadzone.attr,
|
|
&dev_attr_axis_xy_left_deadzone.attr,
|
|
&dev_attr_axis_xyz_deadzone_index.attr,
|
|
NULL
|
|
@@ -619,6 +724,7 @@ static const struct attribute_group axis_xy_left_attr_group = {
|
|
};
|
|
|
|
static struct attribute *axis_xy_right_attrs[] = {
|
|
+ &dev_attr_axis_xy_right_anti_deadzone.attr,
|
|
&dev_attr_axis_xy_right_deadzone.attr,
|
|
&dev_attr_axis_xyz_deadzone_index.attr,
|
|
NULL
|
|
@@ -813,6 +919,9 @@ static ssize_t _gamepad_apply_all(struct hid_device *hdev, struct ally_gamepad_c
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = _gamepad_apply_deadzones(hdev, ally_cfg);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret = _gamepad_apply_js_ADZ(hdev, ally_cfg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
@@ -1059,6 +1168,7 @@ static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
ally_cfg->vibration_intensity[0] = 0x64;
|
|
ally_cfg->vibration_intensity[1] = 0x64;
|
|
_gamepad_set_deadzones_default(ally_cfg);
|
|
+ _gamepad_set_anti_deadzones_default(ally_cfg);
|
|
|
|
drvdata.gamepad_cfg = ally_cfg; // Must asign before attr group setup
|
|
if (sysfs_create_groups(&hdev->dev.kobj, gamepad_device_attr_groups)) {
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index 32ed5caa3759..69f59592dd50 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -28,6 +28,7 @@ enum xpad_cmd {
|
|
xpad_cmd_set_leds = 0x08,
|
|
xpad_cmd_check_ready = 0x0A,
|
|
xpad_cmd_set_turbo = 0x0F,
|
|
+ xpad_cmd_set_adz = 0x18,
|
|
};
|
|
|
|
/* the xpad_cmd determines which feature is set or queried */
|
|
@@ -38,6 +39,7 @@ enum xpad_cmd_len {
|
|
xpad_cmd_len_vibe_intensity = 0x02,
|
|
xpad_cmd_len_leds = 0x0C,
|
|
xpad_cmd_len_turbo = 0x20,
|
|
+ xpad_cmd_len_adz = 0x02,
|
|
};
|
|
|
|
/* Values correspond to the actual HID byte value required */
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 29991f2b1cd7ff8af75db182707856f682f30a7e Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sun, 6 Oct 2024 21:22:40 +1300
|
|
Subject: [PATCH 17/28] hid-asus-ally: add JS response curves
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 103 ++++++++++++++++++++++++++++++++++++
|
|
drivers/hid/hid-asus-ally.h | 38 +++++++++++++
|
|
2 files changed, 141 insertions(+)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index ff1c2cf61e66..aad965e069ee 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -5,10 +5,12 @@
|
|
* Copyright (c) 2023 Luke Jones <luke@ljones.dev>
|
|
*/
|
|
|
|
+#include "linux/compiler_attributes.h"
|
|
#include "linux/device.h"
|
|
#include <linux/platform_data/x86/asus-wmi.h>
|
|
#include <linux/platform_device.h>
|
|
#include "linux/pm.h"
|
|
+#include "linux/printk.h"
|
|
#include "linux/slab.h"
|
|
#include <linux/hid.h>
|
|
#include <linux/types.h>
|
|
@@ -265,6 +267,17 @@ struct deadzone {
|
|
u8 outer;
|
|
};
|
|
|
|
+struct response_curve {
|
|
+ uint8_t move_pct_1;
|
|
+ uint8_t response_pct_1;
|
|
+ uint8_t move_pct_2;
|
|
+ uint8_t response_pct_2;
|
|
+ uint8_t move_pct_3;
|
|
+ uint8_t response_pct_3;
|
|
+ uint8_t move_pct_4;
|
|
+ uint8_t response_pct_4;
|
|
+} __packed;
|
|
+
|
|
/* ROG Ally has many settings related to the gamepad, all using the same n-key endpoint */
|
|
struct ally_gamepad_cfg {
|
|
struct hid_device *hdev;
|
|
@@ -289,6 +302,9 @@ struct ally_gamepad_cfg {
|
|
/* anti-deadzones */
|
|
u8 ls_adz; // left stick
|
|
u8 rs_adz; // right stick
|
|
+ /* joystick response curves */
|
|
+ struct response_curve ls_rc;
|
|
+ struct response_curve rs_rc;
|
|
};
|
|
|
|
/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
@@ -712,10 +728,85 @@ static ssize_t axis_xy_right_anti_deadzone_store(struct device *dev,
|
|
}
|
|
ALLY_DEVICE_ATTR_RW(axis_xy_right_anti_deadzone, anti_deadzone);
|
|
|
|
+/* JS RESPONSE CURVES *****************************************************************************/
|
|
+static void _gamepad_set_js_response_curves_default(struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ struct response_curve *js1_rc = &ally_cfg->ls_rc;
|
|
+ struct response_curve *js2_rc = &ally_cfg->rs_rc;
|
|
+ js1_rc->move_pct_1 = js2_rc->move_pct_1 = 0x16; // 25%
|
|
+ js1_rc->move_pct_2 = js2_rc->move_pct_2 = 0x32; // 50%
|
|
+ js1_rc->move_pct_3 = js2_rc->move_pct_3 = 0x48; // 75%
|
|
+ js1_rc->move_pct_4 = js2_rc->move_pct_4 = 0x64; // 100%
|
|
+ js1_rc->response_pct_1 = js2_rc->response_pct_1 = 0x16;
|
|
+ js1_rc->response_pct_2 = js2_rc->response_pct_2 = 0x32;
|
|
+ js1_rc->response_pct_3 = js2_rc->response_pct_3 = 0x48;
|
|
+ js1_rc->response_pct_4 = js2_rc->response_pct_4 = 0x64;
|
|
+}
|
|
+
|
|
+static ssize_t _gamepad_apply_response_curves(struct hid_device *hdev,
|
|
+ struct ally_gamepad_cfg *ally_cfg)
|
|
+{
|
|
+ u8 *hidbuf;
|
|
+ int ret;
|
|
+
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
|
|
+ memcpy(&hidbuf[2], &ally_cfg->ls_rc, sizeof(ally_cfg->ls_rc));
|
|
+
|
|
+ ret = ally_gamepad_check_ready(hdev);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+ hidbuf[4] = 0x02;
|
|
+ memcpy(&hidbuf[5], &ally_cfg->rs_rc, sizeof(ally_cfg->rs_rc));
|
|
+
|
|
+ ret = ally_gamepad_check_ready(hdev);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0)
|
|
+ goto report_fail;
|
|
+
|
|
+report_fail:
|
|
+ kfree(hidbuf);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+ALLY_JS_RC_POINT(axis_xy_left, move, 1);
|
|
+ALLY_JS_RC_POINT(axis_xy_left, move, 2);
|
|
+ALLY_JS_RC_POINT(axis_xy_left, move, 3);
|
|
+ALLY_JS_RC_POINT(axis_xy_left, move, 4);
|
|
+ALLY_JS_RC_POINT(axis_xy_left, response, 1);
|
|
+ALLY_JS_RC_POINT(axis_xy_left, response, 2);
|
|
+ALLY_JS_RC_POINT(axis_xy_left, response, 3);
|
|
+ALLY_JS_RC_POINT(axis_xy_left, response, 4);
|
|
+
|
|
+ALLY_JS_RC_POINT(axis_xy_right, move, 1);
|
|
+ALLY_JS_RC_POINT(axis_xy_right, move, 2);
|
|
+ALLY_JS_RC_POINT(axis_xy_right, move, 3);
|
|
+ALLY_JS_RC_POINT(axis_xy_right, move, 4);
|
|
+ALLY_JS_RC_POINT(axis_xy_right, response, 1);
|
|
+ALLY_JS_RC_POINT(axis_xy_right, response, 2);
|
|
+ALLY_JS_RC_POINT(axis_xy_right, response, 3);
|
|
+ALLY_JS_RC_POINT(axis_xy_right, response, 4);
|
|
+
|
|
static struct attribute *axis_xy_left_attrs[] = {
|
|
&dev_attr_axis_xy_left_anti_deadzone.attr,
|
|
&dev_attr_axis_xy_left_deadzone.attr,
|
|
&dev_attr_axis_xyz_deadzone_index.attr,
|
|
+ &dev_attr_axis_xy_left_move_1.attr,
|
|
+ &dev_attr_axis_xy_left_move_2.attr,
|
|
+ &dev_attr_axis_xy_left_move_3.attr,
|
|
+ &dev_attr_axis_xy_left_move_4.attr,
|
|
+ &dev_attr_axis_xy_left_response_1.attr,
|
|
+ &dev_attr_axis_xy_left_response_2.attr,
|
|
+ &dev_attr_axis_xy_left_response_3.attr,
|
|
+ &dev_attr_axis_xy_left_response_4.attr,
|
|
NULL
|
|
};
|
|
static const struct attribute_group axis_xy_left_attr_group = {
|
|
@@ -727,6 +818,14 @@ static struct attribute *axis_xy_right_attrs[] = {
|
|
&dev_attr_axis_xy_right_anti_deadzone.attr,
|
|
&dev_attr_axis_xy_right_deadzone.attr,
|
|
&dev_attr_axis_xyz_deadzone_index.attr,
|
|
+ &dev_attr_axis_xy_right_move_1.attr,
|
|
+ &dev_attr_axis_xy_right_move_2.attr,
|
|
+ &dev_attr_axis_xy_right_move_3.attr,
|
|
+ &dev_attr_axis_xy_right_move_4.attr,
|
|
+ &dev_attr_axis_xy_right_response_1.attr,
|
|
+ &dev_attr_axis_xy_right_response_2.attr,
|
|
+ &dev_attr_axis_xy_right_response_3.attr,
|
|
+ &dev_attr_axis_xy_right_response_4.attr,
|
|
NULL
|
|
};
|
|
static const struct attribute_group axis_xy_right_attr_group = {
|
|
@@ -922,6 +1021,9 @@ static ssize_t _gamepad_apply_all(struct hid_device *hdev, struct ally_gamepad_c
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = _gamepad_apply_js_ADZ(hdev, ally_cfg);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ ret =_gamepad_apply_response_curves(hdev, ally_cfg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
@@ -1169,6 +1271,7 @@ static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
ally_cfg->vibration_intensity[1] = 0x64;
|
|
_gamepad_set_deadzones_default(ally_cfg);
|
|
_gamepad_set_anti_deadzones_default(ally_cfg);
|
|
+ _gamepad_set_js_response_curves_default(ally_cfg);
|
|
|
|
drvdata.gamepad_cfg = ally_cfg; // Must asign before attr group setup
|
|
if (sysfs_create_groups(&hdev->dev.kobj, gamepad_device_attr_groups)) {
|
|
diff --git a/drivers/hid/hid-asus-ally.h b/drivers/hid/hid-asus-ally.h
|
|
index 69f59592dd50..c83817589082 100644
|
|
--- a/drivers/hid/hid-asus-ally.h
|
|
+++ b/drivers/hid/hid-asus-ally.h
|
|
@@ -28,6 +28,7 @@ enum xpad_cmd {
|
|
xpad_cmd_set_leds = 0x08,
|
|
xpad_cmd_check_ready = 0x0A,
|
|
xpad_cmd_set_turbo = 0x0F,
|
|
+ xpad_cmd_set_response_curve = 0x13,
|
|
xpad_cmd_set_adz = 0x18,
|
|
};
|
|
|
|
@@ -39,6 +40,7 @@ enum xpad_cmd_len {
|
|
xpad_cmd_len_vibe_intensity = 0x02,
|
|
xpad_cmd_len_leds = 0x0C,
|
|
xpad_cmd_len_turbo = 0x20,
|
|
+ xpad_cmd_len_response_curve = 0x09,
|
|
xpad_cmd_len_adz = 0x02,
|
|
};
|
|
|
|
@@ -309,6 +311,42 @@ enum btn_pair_index {
|
|
ALLY_DEADZONE_STORE(_fname##_deadzone, _mname); \
|
|
ALLY_DEVICE_ATTR_RW(_fname##_deadzone, deadzone)
|
|
|
|
+/* response curve macros */
|
|
+#define ALLY_RESP_CURVE_SHOW(_fname, _mname) \
|
|
+static ssize_t _fname##_show(struct device *dev, \
|
|
+ struct device_attribute *attr, \
|
|
+ char *buf) \
|
|
+ { \
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
|
|
+ if (!drvdata.gamepad_cfg) \
|
|
+ return -ENODEV; \
|
|
+ return sysfs_emit(buf, "%d\n", ally_cfg->ls_rc._mname); \
|
|
+ }
|
|
+
|
|
+#define ALLY_RESP_CURVE_STORE(_fname, _mname) \
|
|
+static ssize_t _fname##_store(struct device *dev, \
|
|
+ struct device_attribute *attr, \
|
|
+ const char *buf, size_t count) \
|
|
+ { \
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
|
|
+ int ret, val; \
|
|
+ if (!drvdata.gamepad_cfg) \
|
|
+ return -ENODEV; \
|
|
+ ret = kstrtoint(buf, 0, &val); \
|
|
+ if (ret) \
|
|
+ return ret; \
|
|
+ if (val < 0 || val > 100) \
|
|
+ return -EINVAL; \
|
|
+ ally_cfg->ls_rc._mname = val; \
|
|
+ return count; \
|
|
+ }
|
|
+
|
|
+/* _point_n must start at 1 */
|
|
+#define ALLY_JS_RC_POINT(_fname, _mname, _num) \
|
|
+ ALLY_RESP_CURVE_SHOW(_fname##_##_mname##_##_num, _mname##_pct_##_num); \
|
|
+ ALLY_RESP_CURVE_STORE(_fname##_##_mname##_##_num, _mname##_pct_##_num); \
|
|
+ ALLY_DEVICE_ATTR_RW(_fname##_##_mname##_##_num, curve_##_mname##_pct_##_num)
|
|
+
|
|
#define ALLY_BTN_ATTRS_GROUP(_name, _fname) \
|
|
static struct attribute *_fname##_attrs[] = { \
|
|
&dev_attr_##_fname.attr, \
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From a46fa579d620183c76a594bf70df3864ee7c3f21 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Thu, 10 Oct 2024 11:15:36 +1300
|
|
Subject: [PATCH 18/28] hid-asus-ally: add calibrations (wip)
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 95 +++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 95 insertions(+)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index aad965e069ee..a1015713c245 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -278,6 +278,28 @@ struct response_curve {
|
|
uint8_t response_pct_4;
|
|
} __packed;
|
|
|
|
+struct js_axis_calibrations {
|
|
+ uint16_t left_y_stable;
|
|
+ uint16_t left_y_min;
|
|
+ uint16_t left_y_max;
|
|
+ uint16_t left_x_stable;
|
|
+ uint16_t left_x_min;
|
|
+ uint16_t left_x_max;
|
|
+ uint16_t right_y_stable;
|
|
+ uint16_t right_y_min;
|
|
+ uint16_t right_y_max;
|
|
+ uint16_t right_x_stable;
|
|
+ uint16_t right_x_min;
|
|
+ uint16_t right_x_max;
|
|
+} __packed;
|
|
+
|
|
+struct tr_axis_calibrations {
|
|
+ uint16_t left_stable;
|
|
+ uint16_t left_max;
|
|
+ uint16_t right_stable;
|
|
+ uint16_t right_max;
|
|
+} __packed;
|
|
+
|
|
/* ROG Ally has many settings related to the gamepad, all using the same n-key endpoint */
|
|
struct ally_gamepad_cfg {
|
|
struct hid_device *hdev;
|
|
@@ -305,6 +327,9 @@ struct ally_gamepad_cfg {
|
|
/* joystick response curves */
|
|
struct response_curve ls_rc;
|
|
struct response_curve rs_rc;
|
|
+
|
|
+ struct js_axis_calibrations js_cal;
|
|
+ struct tr_axis_calibrations tr_cal;
|
|
};
|
|
|
|
/* The hatswitch outputs integers, we use them to index this X|Y pair */
|
|
@@ -382,6 +407,18 @@ static struct ally_drvdata {
|
|
struct ally_rgb_data led_rgb_data;
|
|
} drvdata;
|
|
|
|
+static void reverse_bytes_in_pairs(u8 *buf, size_t size) {
|
|
+ uint16_t *word_ptr;
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < size; i += 2) {
|
|
+ if (i + 1 < size) {
|
|
+ word_ptr = (uint16_t *)&buf[i];
|
|
+ *word_ptr = cpu_to_be16(*word_ptr);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* asus_dev_set_report - send set report request to device.
|
|
*
|
|
@@ -795,6 +832,63 @@ ALLY_JS_RC_POINT(axis_xy_right, response, 2);
|
|
ALLY_JS_RC_POINT(axis_xy_right, response, 3);
|
|
ALLY_JS_RC_POINT(axis_xy_right, response, 4);
|
|
|
|
+/* CALIBRATIONS ***********************************************************************************/
|
|
+static int gamepad_get_calibration(struct hid_device *hdev)
|
|
+{
|
|
+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
|
|
+ u8 *hidbuf;
|
|
+ int ret, i;
|
|
+
|
|
+ if (!drvdata.gamepad_cfg)
|
|
+ return -ENODEV;
|
|
+
|
|
+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
|
|
+ if (!hidbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (i = 0; i < 2; i++) {
|
|
+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
|
|
+ hidbuf[1] = 0xD0;
|
|
+ hidbuf[2] = 0x03;
|
|
+ hidbuf[3] = i + 1; // 0x01 JS, 0x02 TR
|
|
+ hidbuf[4] = 0x20;
|
|
+
|
|
+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0) {
|
|
+ hid_warn(hdev, "ROG Ally check failed set report: %d\n", ret);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ memset(hidbuf, 0, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ ret = asus_dev_get_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
|
|
+ if (ret < 0 || hidbuf[5] != 1) {
|
|
+ hid_warn(hdev, "ROG Ally check failed get report: %d\n", ret);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (i == 0) {
|
|
+ /* Joystick calibration */
|
|
+ reverse_bytes_in_pairs(&hidbuf[6], sizeof(struct js_axis_calibrations));
|
|
+ ally_cfg->js_cal = *(struct js_axis_calibrations *)&hidbuf[6];
|
|
+ print_hex_dump(KERN_INFO, "HID Buffer JS: ", DUMP_PREFIX_OFFSET, 16, 1, hidbuf, 32, true);
|
|
+ struct js_axis_calibrations *cal = &drvdata.gamepad_cfg->js_cal;
|
|
+ pr_err("LS_CAL: X: %d, Min: %d, Max: %d", cal->left_x_stable, cal->left_x_min, cal->left_x_max);
|
|
+ pr_err("LS_CAL: Y: %d, Min: %d, Max: %d", cal->left_y_stable, cal->left_y_min, cal->left_y_max);
|
|
+ pr_err("RS_CAL: X: %d, Min: %d, Max: %d", cal->right_x_stable, cal->right_x_min, cal->right_x_max);
|
|
+ pr_err("RS_CAL: Y: %d, Min: %d, Max: %d", cal->right_y_stable, cal->right_y_min, cal->right_y_max);
|
|
+ } else {
|
|
+ /* Trigger calibration */
|
|
+ reverse_bytes_in_pairs(&hidbuf[6], sizeof(struct tr_axis_calibrations));
|
|
+ ally_cfg->tr_cal = *(struct tr_axis_calibrations *)&hidbuf[6];
|
|
+ print_hex_dump(KERN_INFO, "HID Buffer TR: ", DUMP_PREFIX_OFFSET, 16, 1, hidbuf, 32, true);
|
|
+ }
|
|
+ }
|
|
+
|
|
+cleanup:
|
|
+ kfree(hidbuf);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static struct attribute *axis_xy_left_attrs[] = {
|
|
&dev_attr_axis_xy_left_anti_deadzone.attr,
|
|
&dev_attr_axis_xy_left_deadzone.attr,
|
|
@@ -1266,6 +1360,7 @@ static struct ally_gamepad_cfg *ally_gamepad_cfg_create(struct hid_device *hdev)
|
|
ally_cfg->key_mapping[ally_cfg->mode - 1].btn_m1.button = BTN_KB_M1;
|
|
ally_cfg->key_mapping[ally_cfg->mode - 1].btn_m2.button = BTN_KB_M2;
|
|
_gamepad_apply_btn_pair(hdev, ally_cfg, btn_pair_m1_m2);
|
|
+ gamepad_get_calibration(hdev);
|
|
|
|
ally_cfg->vibration_intensity[0] = 0x64;
|
|
ally_cfg->vibration_intensity[1] = 0x64;
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From ce9ddc7e913a1d52fa4fb4b6e1330e2746b0a8e9 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Wed, 6 Nov 2024 00:27:03 +0300
|
|
Subject: [PATCH 19/28] debug by default
|
|
|
|
---
|
|
drivers/hid/hid-asus-ally.c | 2 ++
|
|
1 file changed, 2 insertions(+)
|
|
|
|
diff --git a/drivers/hid/hid-asus-ally.c b/drivers/hid/hid-asus-ally.c
|
|
index a1015713c245..c10121ebcbb8 100644
|
|
--- a/drivers/hid/hid-asus-ally.c
|
|
+++ b/drivers/hid/hid-asus-ally.c
|
|
@@ -21,6 +21,8 @@
|
|
#include "hid-ids.h"
|
|
#include "hid-asus-ally.h"
|
|
|
|
+#define DEBUG
|
|
+
|
|
#define READY_MAX_TRIES 3
|
|
#define FEATURE_REPORT_ID 0x0d
|
|
#define FEATURE_ROG_ALLY_REPORT_ID 0x5a
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From c80e393a6ffe8c8085107bbe9b2fcb0a9eff199a Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sun, 22 Sep 2024 21:39:43 +1200
|
|
Subject: [PATCH 20/28] platform/x86: asus-armoury: move existing tunings to
|
|
asus-armoury module
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
The fw_attributes_class provides a much cleaner interface to all of the
|
|
attributes introduced to asus-wmi. This patch moves all of these extra
|
|
attributes over to fw_attributes_class, and shifts the bulk of these
|
|
definitions to a new kernel module to reduce the clutter of asus-wmi
|
|
with the intention of deprecating the asus-wmi attributes in future.
|
|
|
|
The work applies only to WMI methods which don't have a clearly defined
|
|
place within the sysfs and as a result ended up lumped together in
|
|
/sys/devices/platform/asus-nb-wmi/ with no standard API.
|
|
|
|
Where possible the fw attrs now implement defaults, min, max, scalar,
|
|
choices, etc. As en example dgpu_disable becomes:
|
|
|
|
/sys/class/firmware-attributes/asus-armoury/attributes/dgpu_disable/
|
|
├── current_value
|
|
├── display_name
|
|
├── possible_values
|
|
└── type
|
|
|
|
as do other attributes.
|
|
|
|
The ppt_* based attributes are removed in this initial patch as the
|
|
implementation is somewhat broken due to the WMI methods requiring a
|
|
set of limits on the values accepted (which is not provided by WMI).
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/platform/x86/Kconfig | 12 +
|
|
drivers/platform/x86/Makefile | 1 +
|
|
drivers/platform/x86/asus-armoury.c | 553 +++++++++++++++++++++
|
|
drivers/platform/x86/asus-armoury.h | 147 ++++++
|
|
drivers/platform/x86/asus-wmi.c | 4 -
|
|
include/linux/platform_data/x86/asus-wmi.h | 3 +
|
|
6 files changed, 716 insertions(+), 4 deletions(-)
|
|
create mode 100644 drivers/platform/x86/asus-armoury.c
|
|
create mode 100644 drivers/platform/x86/asus-armoury.h
|
|
|
|
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
|
|
index 0258dd879d64..294364cc7478 100644
|
|
--- a/drivers/platform/x86/Kconfig
|
|
+++ b/drivers/platform/x86/Kconfig
|
|
@@ -267,6 +267,18 @@ config ASUS_WIRELESS
|
|
If you choose to compile this driver as a module the module will be
|
|
called asus-wireless.
|
|
|
|
+config ASUS_ARMOURY
|
|
+ tristate "ASUS Armoury driver"
|
|
+ depends on ASUS_WMI
|
|
+ select FW_ATTR_CLASS
|
|
+ help
|
|
+ Say Y here if you have a WMI aware Asus machine and would like to use the
|
|
+ firmware_attributes API to control various settings typically exposed in
|
|
+ the ASUS Armoury Crate application available on Windows.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the module will
|
|
+ be called asus-armoury.
|
|
+
|
|
config ASUS_WMI
|
|
tristate "ASUS WMI Driver"
|
|
depends on ACPI_WMI
|
|
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
|
|
index e1b142947067..fe3e7e7dede8 100644
|
|
--- a/drivers/platform/x86/Makefile
|
|
+++ b/drivers/platform/x86/Makefile
|
|
@@ -32,6 +32,7 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
|
|
# ASUS
|
|
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
|
|
obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o
|
|
+obj-$(CONFIG_ASUS_ARMOURY) += asus-armoury.o
|
|
obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
|
|
obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
|
|
obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o
|
|
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
|
|
new file mode 100644
|
|
index 000000000000..d2e8c21d62dc
|
|
--- /dev/null
|
|
+++ b/drivers/platform/x86/asus-armoury.c
|
|
@@ -0,0 +1,553 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+/*
|
|
+ * Asus Armoury (WMI) attributes driver. This driver uses the fw_attributes
|
|
+ * class to expose the various WMI functions that many gaming and some
|
|
+ * non-gaming ASUS laptops have available.
|
|
+ * These typically don't fit anywhere else in the sysfs such as under LED class,
|
|
+ * hwmon or other, and are set in Windows using the ASUS Armoury Crate tool.
|
|
+ *
|
|
+ * Copyright(C) 2024 Luke Jones <luke@ljones.dev>
|
|
+ */
|
|
+
|
|
+#include "linux/cleanup.h"
|
|
+#include <linux/bitfield.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/dmi.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/kmod.h>
|
|
+#include <linux/kobject.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/platform_data/x86/asus-wmi.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/acpi.h>
|
|
+
|
|
+#include "asus-armoury.h"
|
|
+#include "firmware_attributes_class.h"
|
|
+
|
|
+#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
|
|
+
|
|
+#define ASUS_MINI_LED_MODE_MASK 0x03
|
|
+/* Standard modes for devices with only on/off */
|
|
+#define ASUS_MINI_LED_OFF 0x00
|
|
+#define ASUS_MINI_LED_ON 0x01
|
|
+/* Like "on" but the effect is more vibrant or brighter */
|
|
+#define ASUS_MINI_LED_STRONG_MODE 0x02
|
|
+/* New modes for devices with 3 mini-led mode types */
|
|
+#define ASUS_MINI_LED_2024_WEAK 0x00
|
|
+#define ASUS_MINI_LED_2024_STRONG 0x01
|
|
+#define ASUS_MINI_LED_2024_OFF 0x02
|
|
+
|
|
+struct asus_armoury_priv {
|
|
+ struct device *fw_attr_dev;
|
|
+ struct kset *fw_attr_kset;
|
|
+
|
|
+ u32 mini_led_dev_id;
|
|
+ u32 gpu_mux_dev_id;
|
|
+
|
|
+ struct mutex mutex;
|
|
+};
|
|
+
|
|
+static struct asus_armoury_priv asus_armoury = {
|
|
+ .mutex = __MUTEX_INITIALIZER(asus_armoury.mutex)
|
|
+};
|
|
+
|
|
+struct fw_attrs_group {
|
|
+ bool pending_reboot;
|
|
+};
|
|
+
|
|
+static struct fw_attrs_group fw_attrs = {
|
|
+ .pending_reboot = false,
|
|
+};
|
|
+
|
|
+struct asus_attr_group {
|
|
+ const struct attribute_group *attr_group;
|
|
+ u32 wmi_devid;
|
|
+};
|
|
+
|
|
+static bool asus_wmi_is_present(u32 dev_id)
|
|
+{
|
|
+ u32 retval;
|
|
+ int status;
|
|
+
|
|
+ status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, &retval);
|
|
+ pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval);
|
|
+
|
|
+ return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
|
|
+}
|
|
+
|
|
+static void asus_set_reboot_and_signal_event(void)
|
|
+{
|
|
+ fw_attrs.pending_reboot = true;
|
|
+ kobject_uevent(&asus_armoury.fw_attr_dev->kobj, KOBJ_CHANGE);
|
|
+}
|
|
+
|
|
+static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ return sysfs_emit(buf, "%d\n", fw_attrs.pending_reboot);
|
|
+}
|
|
+
|
|
+static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
|
|
+
|
|
+static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
|
|
+{
|
|
+ return !strcmp(attr->attr.name, "gpu_mux_mode");
|
|
+}
|
|
+
|
|
+static int armoury_wmi_set_devstate(struct kobj_attribute *attr, u32 value, u32 wmi_dev)
|
|
+{
|
|
+ u32 result;
|
|
+ int err;
|
|
+
|
|
+ guard(mutex)(&asus_armoury.mutex);
|
|
+ err = asus_wmi_set_devstate(wmi_dev, value, &result);
|
|
+ if (err) {
|
|
+ pr_err("Failed to set %s: %d\n", attr->attr.name, err);
|
|
+ return err;
|
|
+ }
|
|
+ /*
|
|
+ * !1 is usually considered a fail by ASUS, but some WMI methods do use > 1
|
|
+ * to return a status code or similar.
|
|
+ */
|
|
+ if (result < 1) {
|
|
+ pr_err("Failed to set %s: (result): 0x%x\n", attr->attr.name, result);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * attr_int_store() - Send an int to wmi method, checks if within min/max exclusive.
|
|
+ * @kobj: Pointer to the driver object.
|
|
+ * @attr: Pointer to the attribute calling this function.
|
|
+ * @buf: The buffer to read from, this is parsed to `int` type.
|
|
+ * @count: Required by sysfs attribute macros, pass in from the callee attr.
|
|
+ * @min: Minimum accepted value. Below this returns -EINVAL.
|
|
+ * @max: Maximum accepted value. Above this returns -EINVAL.
|
|
+ * @store_value: Pointer to where the parsed value should be stored.
|
|
+ * @wmi_dev: The WMI function ID to use.
|
|
+ *
|
|
+ * This function is intended to be generic so it can be called from any "_store"
|
|
+ * attribute which works only with integers. The integer to be sent to the WMI method
|
|
+ * is range checked and an error returned if out of range.
|
|
+ *
|
|
+ * If the value is valid and WMI is success, then the sysfs attribute is notified
|
|
+ * and if asus_bios_requires_reboot() is true then reboot attribute is also notified.
|
|
+ *
|
|
+ * Returns: Either count, or an error.
|
|
+ */
|
|
+static ssize_t attr_uint_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
|
|
+ size_t count, u32 min, u32 max, u32 *store_value, u32 wmi_dev)
|
|
+{
|
|
+ u32 value;
|
|
+ int err;
|
|
+
|
|
+ err = kstrtouint(buf, 10, &value);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (value < min || value > max)
|
|
+ return -EINVAL;
|
|
+
|
|
+ err = armoury_wmi_set_devstate(attr, value, wmi_dev);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (store_value != NULL)
|
|
+ *store_value = value;
|
|
+ sysfs_notify(kobj, NULL, attr->attr.name);
|
|
+
|
|
+ if (asus_bios_requires_reboot(attr))
|
|
+ asus_set_reboot_and_signal_event();
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+/* Mini-LED mode **************************************************************/
|
|
+static ssize_t mini_led_mode_current_value_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ u32 value;
|
|
+ int err;
|
|
+
|
|
+ err = asus_wmi_get_devstate_dsts(asus_armoury.mini_led_dev_id, &value);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ value &= ASUS_MINI_LED_MODE_MASK;
|
|
+
|
|
+ /*
|
|
+ * Remap the mode values to match previous generation mini-LED. The last gen
|
|
+ * WMI 0 == off, while on this version WMI 2 == off (flipped).
|
|
+ */
|
|
+ if (asus_armoury.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
|
|
+ switch (value) {
|
|
+ case ASUS_MINI_LED_2024_WEAK:
|
|
+ value = ASUS_MINI_LED_ON;
|
|
+ break;
|
|
+ case ASUS_MINI_LED_2024_STRONG:
|
|
+ value = ASUS_MINI_LED_STRONG_MODE;
|
|
+ break;
|
|
+ case ASUS_MINI_LED_2024_OFF:
|
|
+ value = ASUS_MINI_LED_OFF;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return sysfs_emit(buf, "%u\n", value);
|
|
+}
|
|
+
|
|
+static ssize_t mini_led_mode_current_value_store(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ u32 mode;
|
|
+ int err;
|
|
+
|
|
+ err = kstrtou32(buf, 10, &mode);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (asus_armoury.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE &&
|
|
+ mode > ASUS_MINI_LED_ON)
|
|
+ return -EINVAL;
|
|
+ if (asus_armoury.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2 &&
|
|
+ mode > ASUS_MINI_LED_STRONG_MODE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /*
|
|
+ * Remap the mode values so expected behaviour is the same as the last
|
|
+ * generation of mini-LED with 0 == off, 1 == on.
|
|
+ */
|
|
+ if (asus_armoury.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
|
|
+ switch (mode) {
|
|
+ case ASUS_MINI_LED_OFF:
|
|
+ mode = ASUS_MINI_LED_2024_OFF;
|
|
+ break;
|
|
+ case ASUS_MINI_LED_ON:
|
|
+ mode = ASUS_MINI_LED_2024_WEAK;
|
|
+ break;
|
|
+ case ASUS_MINI_LED_STRONG_MODE:
|
|
+ mode = ASUS_MINI_LED_2024_STRONG;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ err = armoury_wmi_set_devstate(attr, mode, asus_armoury.mini_led_dev_id);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ sysfs_notify(kobj, NULL, attr->attr.name);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t mini_led_mode_possible_values_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ switch (asus_armoury.mini_led_dev_id) {
|
|
+ case ASUS_WMI_DEVID_MINI_LED_MODE:
|
|
+ return sysfs_emit(buf, "0;1\n");
|
|
+ case ASUS_WMI_DEVID_MINI_LED_MODE2:
|
|
+ return sysfs_emit(buf, "0;1;2\n");
|
|
+ }
|
|
+
|
|
+ return sysfs_emit(buf, "0\n");
|
|
+}
|
|
+
|
|
+ATTR_GROUP_ENUM_CUSTOM(mini_led_mode, "mini_led_mode", "Set the mini-LED backlight mode");
|
|
+
|
|
+static ssize_t gpu_mux_mode_current_value_store(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ int result, err;
|
|
+ u32 optimus;
|
|
+
|
|
+ err = kstrtou32(buf, 10, &optimus);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (optimus > 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU)) {
|
|
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_DGPU, &result);
|
|
+ if (err)
|
|
+ return err;
|
|
+ if (result && !optimus) {
|
|
+ err = -ENODEV;
|
|
+ pr_warn("Can not switch MUX to dGPU mode when dGPU is disabled: %02X %02X %d\n",
|
|
+ result, optimus, err);
|
|
+ return err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU)) {
|
|
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU, &result);
|
|
+ if (err)
|
|
+ return err;
|
|
+ if (result && !optimus) {
|
|
+ err = -ENODEV;
|
|
+ pr_warn("Can not switch MUX to dGPU mode when eGPU is enabled: %d\n",
|
|
+ err);
|
|
+ return err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ err = armoury_wmi_set_devstate(attr, optimus, asus_armoury.gpu_mux_dev_id);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ sysfs_notify(kobj, NULL, attr->attr.name);
|
|
+ asus_set_reboot_and_signal_event();
|
|
+
|
|
+ return count;
|
|
+}
|
|
+WMI_SHOW_INT(gpu_mux_mode_current_value, "%d\n", asus_armoury.gpu_mux_dev_id);
|
|
+ATTR_GROUP_BOOL_CUSTOM(gpu_mux_mode, "gpu_mux_mode", "Set the GPU display MUX mode");
|
|
+
|
|
+/*
|
|
+ * A user may be required to store the value twice, typical store first, then
|
|
+ * rescan PCI bus to activate power, then store a second time to save correctly.
|
|
+ */
|
|
+static ssize_t dgpu_disable_current_value_store(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ int result, err;
|
|
+ u32 disable;
|
|
+
|
|
+ err = kstrtou32(buf, 10, &disable);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (disable > 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (asus_armoury.gpu_mux_dev_id) {
|
|
+ err = asus_wmi_get_devstate_dsts(asus_armoury.gpu_mux_dev_id, &result);
|
|
+ if (err)
|
|
+ return err;
|
|
+ if (!result && disable) {
|
|
+ err = -ENODEV;
|
|
+ pr_warn("Can not disable dGPU when the MUX is in dGPU mode: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+ // TODO: handle a > 1 result, shouold do a PCI rescan and run again
|
|
+ }
|
|
+
|
|
+ err = armoury_wmi_set_devstate(attr, disable, ASUS_WMI_DEVID_DGPU);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ sysfs_notify(kobj, NULL, attr->attr.name);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+WMI_SHOW_INT(dgpu_disable_current_value, "%d\n", ASUS_WMI_DEVID_DGPU);
|
|
+ATTR_GROUP_BOOL_CUSTOM(dgpu_disable, "dgpu_disable", "Disable the dGPU");
|
|
+
|
|
+/* The ACPI call to enable the eGPU also disables the internal dGPU */
|
|
+static ssize_t egpu_enable_current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int result, err;
|
|
+ u32 enable;
|
|
+
|
|
+ err = kstrtou32(buf, 10, &enable);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (enable > 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU_CONNECTED, &result);
|
|
+ if (err) {
|
|
+ pr_warn("Failed to get eGPU connection status: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (asus_armoury.gpu_mux_dev_id) {
|
|
+ err = asus_wmi_get_devstate_dsts(asus_armoury.gpu_mux_dev_id, &result);
|
|
+ if (err) {
|
|
+ pr_warn("Failed to get GPU MUX status: %d\n", result);
|
|
+ return result;
|
|
+ }
|
|
+ if (!result && enable) {
|
|
+ err = -ENODEV;
|
|
+ pr_warn("Can not enable eGPU when the MUX is in dGPU mode: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ err = armoury_wmi_set_devstate(attr, enable, ASUS_WMI_DEVID_EGPU);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ sysfs_notify(kobj, NULL, attr->attr.name);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+WMI_SHOW_INT(egpu_enable_current_value, "%d\n", ASUS_WMI_DEVID_EGPU);
|
|
+ATTR_GROUP_BOOL_CUSTOM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)");
|
|
+
|
|
+/* Simple attribute creation */
|
|
+ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2",
|
|
+ "Show the current mode of charging");
|
|
+
|
|
+ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND,
|
|
+ "Set the boot POST sound");
|
|
+ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE,
|
|
+ "Set MCU powersaving mode");
|
|
+ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD,
|
|
+ "Set the panel refresh overdrive");
|
|
+ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
|
|
+ "Show the eGPU connection status");
|
|
+
|
|
+/* If an attribute does not require any special case handling add it here */
|
|
+static const struct asus_attr_group armoury_attr_groups[] = {
|
|
+ { &egpu_connected_attr_group, ASUS_WMI_DEVID_EGPU_CONNECTED },
|
|
+ { &egpu_enable_attr_group, ASUS_WMI_DEVID_EGPU },
|
|
+ { &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU },
|
|
+
|
|
+ { &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE },
|
|
+ { &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND },
|
|
+ { &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE },
|
|
+ { &panel_od_attr_group, ASUS_WMI_DEVID_PANEL_OD },
|
|
+};
|
|
+
|
|
+static int asus_fw_attr_add(void)
|
|
+{
|
|
+ int err, i;
|
|
+
|
|
+ asus_armoury.fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
|
|
+ NULL, "%s", DRIVER_NAME);
|
|
+ if (IS_ERR(asus_armoury.fw_attr_dev)) {
|
|
+ err = PTR_ERR(asus_armoury.fw_attr_dev);
|
|
+ goto fail_class_get;
|
|
+ }
|
|
+
|
|
+ asus_armoury.fw_attr_kset = kset_create_and_add("attributes", NULL,
|
|
+ &asus_armoury.fw_attr_dev->kobj);
|
|
+ if (!asus_armoury.fw_attr_kset) {
|
|
+ err = -ENOMEM;
|
|
+ goto err_destroy_classdev;
|
|
+ }
|
|
+
|
|
+ err = sysfs_create_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
|
|
+ if (err) {
|
|
+ pr_err("Failed to create sysfs level attributes\n");
|
|
+ goto err_destroy_kset;
|
|
+ }
|
|
+
|
|
+ asus_armoury.mini_led_dev_id = 0;
|
|
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE)) {
|
|
+ asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
|
|
+ } else if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE2)) {
|
|
+ asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
|
|
+ }
|
|
+
|
|
+ if (asus_armoury.mini_led_dev_id) {
|
|
+ err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
|
|
+ if (err) {
|
|
+ pr_err("Failed to create sysfs-group for mini_led\n");
|
|
+ goto err_remove_file;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ asus_armoury.gpu_mux_dev_id = 0;
|
|
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX)) {
|
|
+ asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX;
|
|
+ } else if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX_VIVO)) {
|
|
+ asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO;
|
|
+ }
|
|
+
|
|
+ if (asus_armoury.gpu_mux_dev_id) {
|
|
+ err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
|
|
+ if (err) {
|
|
+ pr_err("Failed to create sysfs-group for gpu_mux\n");
|
|
+ goto err_remove_mini_led_group;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(armoury_attr_groups); i++) {
|
|
+ if (!asus_wmi_is_present(armoury_attr_groups[i].wmi_devid))
|
|
+ continue;
|
|
+
|
|
+ err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
|
|
+ armoury_attr_groups[i].attr_group);
|
|
+ if (err) {
|
|
+ pr_err("Failed to create sysfs-group for %s\n",
|
|
+ armoury_attr_groups[i].attr_group->name);
|
|
+ goto err_remove_groups;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_remove_groups:
|
|
+ while (--i >= 0) {
|
|
+ if (asus_wmi_is_present(armoury_attr_groups[i].wmi_devid))
|
|
+ sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, armoury_attr_groups[i].attr_group);
|
|
+ }
|
|
+ sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
|
|
+err_remove_mini_led_group:
|
|
+ sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
|
|
+err_remove_file:
|
|
+ sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
|
|
+err_destroy_kset:
|
|
+ kset_unregister(asus_armoury.fw_attr_kset);
|
|
+err_destroy_classdev:
|
|
+fail_class_get:
|
|
+ device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Init / exit ****************************************************************/
|
|
+
|
|
+static int __init asus_fw_init(void)
|
|
+{
|
|
+ char *wmi_uid;
|
|
+ int err;
|
|
+
|
|
+ wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
|
|
+ if (!wmi_uid)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /*
|
|
+ * if equal to "ASUSWMI" then it's DCTS that can't be used for this
|
|
+ * driver, DSTS is required.
|
|
+ */
|
|
+ if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI))
|
|
+ return -ENODEV;
|
|
+
|
|
+ err = asus_fw_attr_add();
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit asus_fw_exit(void)
|
|
+{
|
|
+ mutex_lock(&asus_armoury.mutex);
|
|
+
|
|
+ sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
|
|
+ kset_unregister(asus_armoury.fw_attr_kset);
|
|
+ device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
|
+
|
|
+ mutex_unlock(&asus_armoury.mutex);
|
|
+}
|
|
+
|
|
+module_init(asus_fw_init);
|
|
+module_exit(asus_fw_exit);
|
|
+
|
|
+MODULE_IMPORT_NS("ASUS_WMI");
|
|
+MODULE_AUTHOR("Luke Jones <luke@ljones.dev>");
|
|
+MODULE_DESCRIPTION("ASUS BIOS Configuration Driver");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_ALIAS("wmi:" ASUS_NB_WMI_EVENT_GUID);
|
|
diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h
|
|
new file mode 100644
|
|
index 000000000000..440f41c5df3b
|
|
--- /dev/null
|
|
+++ b/drivers/platform/x86/asus-armoury.h
|
|
@@ -0,0 +1,147 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0
|
|
+ *
|
|
+ * Definitions for kernel modules using asus-armoury driver
|
|
+ *
|
|
+ * Copyright (c) 2024 Luke Jones <luke@ljones.dev>
|
|
+ */
|
|
+
|
|
+#ifndef _ASUS_ARMOURY_H_
|
|
+#define _ASUS_ARMOURY_H_
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/platform_device.h>
|
|
+
|
|
+#define DRIVER_NAME "asus-armoury"
|
|
+
|
|
+static ssize_t attr_uint_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ const char *buf, size_t count, u32 min, u32 max,
|
|
+ u32 *store_value, u32 wmi_dev);
|
|
+
|
|
+static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return sysfs_emit(buf, "enumeration\n");
|
|
+}
|
|
+
|
|
+#define __ASUS_ATTR_RO(_func, _name) \
|
|
+ { \
|
|
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
|
|
+ .show = _func##_##_name##_show, \
|
|
+ }
|
|
+
|
|
+#define __ASUS_ATTR_RO_AS(_name, _show) \
|
|
+ { \
|
|
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
|
|
+ .show = _show, \
|
|
+ }
|
|
+
|
|
+#define __ASUS_ATTR_RW(_func, _name) \
|
|
+ __ATTR(_name, 0644, _func##_##_name##_show, _func##_##_name##_store)
|
|
+
|
|
+#define __WMI_STORE_INT(_attr, _min, _max, _wmi) \
|
|
+ static ssize_t _attr##_store(struct kobject *kobj, \
|
|
+ struct kobj_attribute *attr, \
|
|
+ const char *buf, size_t count) \
|
|
+ { \
|
|
+ return attr_uint_store(kobj, attr, buf, count, _min, _max, \
|
|
+ NULL, _wmi); \
|
|
+ }
|
|
+
|
|
+#define WMI_SHOW_INT(_attr, _fmt, _wmi) \
|
|
+ static ssize_t _attr##_show(struct kobject *kobj, \
|
|
+ struct kobj_attribute *attr, char *buf) \
|
|
+ { \
|
|
+ u32 result; \
|
|
+ int err; \
|
|
+ \
|
|
+ err = asus_wmi_get_devstate_dsts(_wmi, &result); \
|
|
+ if (err) \
|
|
+ return err; \
|
|
+ return sysfs_emit(buf, _fmt, \
|
|
+ result & ~ASUS_WMI_DSTS_PRESENCE_BIT); \
|
|
+ }
|
|
+
|
|
+/* Create functions and attributes for use in other macros or on their own */
|
|
+
|
|
+#define __ATTR_CURRENT_INT_RO(_attr, _wmi) \
|
|
+ WMI_SHOW_INT(_attr##_current_value, "%d\n", _wmi); \
|
|
+ static struct kobj_attribute attr_##_attr##_current_value = \
|
|
+ __ASUS_ATTR_RO(_attr, current_value)
|
|
+
|
|
+#define __ATTR_CURRENT_INT_RW(_attr, _minv, _maxv, _wmi) \
|
|
+ __WMI_STORE_INT(_attr##_current_value, _minv, _maxv, _wmi); \
|
|
+ WMI_SHOW_INT(_attr##_current_value, "%d\n", _wmi); \
|
|
+ static struct kobj_attribute attr_##_attr##_current_value = \
|
|
+ __ASUS_ATTR_RW(_attr, current_value)
|
|
+
|
|
+/* Shows a formatted static variable */
|
|
+#define __ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \
|
|
+ static ssize_t _attrname##_##_prop##_show( \
|
|
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf) \
|
|
+ { \
|
|
+ return sysfs_emit(buf, _fmt, _val); \
|
|
+ } \
|
|
+ static struct kobj_attribute attr_##_attrname##_##_prop = \
|
|
+ __ASUS_ATTR_RO(_attrname, _prop)
|
|
+
|
|
+/* Boolean style enumeration, base macro. Requires adding show/store */
|
|
+#define __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname) \
|
|
+ __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
|
|
+ __ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \
|
|
+ static struct kobj_attribute attr_##_attrname##_type = \
|
|
+ __ASUS_ATTR_RO_AS(type, enum_type_show); \
|
|
+ static struct attribute *_attrname##_attrs[] = { \
|
|
+ &attr_##_attrname##_current_value.attr, \
|
|
+ &attr_##_attrname##_display_name.attr, \
|
|
+ &attr_##_attrname##_possible_values.attr, \
|
|
+ &attr_##_attrname##_type.attr, \
|
|
+ NULL \
|
|
+ }; \
|
|
+ static const struct attribute_group _attrname##_attr_group = { \
|
|
+ .name = _fsname, .attrs = _attrname##_attrs \
|
|
+ }
|
|
+
|
|
+#define ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \
|
|
+ __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
|
|
+ __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname)
|
|
+
|
|
+#define ATTR_GROUP_BOOL_RW(_attrname, _fsname, _wmi, _dispname) \
|
|
+ __ATTR_CURRENT_INT_RW(_attrname, 0, 1, _wmi); \
|
|
+ __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname)
|
|
+
|
|
+/*
|
|
+ * Requires <name>_current_value_show(), <name>_current_value_show()
|
|
+ */
|
|
+#define ATTR_GROUP_BOOL_CUSTOM(_attrname, _fsname, _dispname) \
|
|
+ static struct kobj_attribute attr_##_attrname##_current_value = \
|
|
+ __ASUS_ATTR_RW(_attrname, current_value); \
|
|
+ __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname)
|
|
+
|
|
+#define ATTR_GROUP_ENUM_INT_RO(_attrname, _fsname, _wmi, _possible, _dispname) \
|
|
+ __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
|
|
+ __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname)
|
|
+
|
|
+/*
|
|
+ * Requires <name>_current_value_show(), <name>_current_value_show()
|
|
+ * and <name>_possible_values_show()
|
|
+ */
|
|
+#define ATTR_GROUP_ENUM_CUSTOM(_attrname, _fsname, _dispname) \
|
|
+ __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
|
|
+ static struct kobj_attribute attr_##_attrname##_current_value = \
|
|
+ __ASUS_ATTR_RW(_attrname, current_value); \
|
|
+ static struct kobj_attribute attr_##_attrname##_possible_values = \
|
|
+ __ASUS_ATTR_RO(_attrname, possible_values); \
|
|
+ static struct kobj_attribute attr_##_attrname##_type = \
|
|
+ __ASUS_ATTR_RO_AS(type, enum_type_show); \
|
|
+ static struct attribute *_attrname##_attrs[] = { \
|
|
+ &attr_##_attrname##_current_value.attr, \
|
|
+ &attr_##_attrname##_display_name.attr, \
|
|
+ &attr_##_attrname##_possible_values.attr, \
|
|
+ &attr_##_attrname##_type.attr, \
|
|
+ NULL \
|
|
+ }; \
|
|
+ static const struct attribute_group _attrname##_attr_group = { \
|
|
+ .name = _fsname, .attrs = _attrname##_attrs \
|
|
+ }
|
|
+
|
|
+#endif /* _ASUS_ARMOURY_H_ */
|
|
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
|
index c02d9fa89799..b91cb5538b2d 100644
|
|
--- a/drivers/platform/x86/asus-wmi.c
|
|
+++ b/drivers/platform/x86/asus-wmi.c
|
|
@@ -55,8 +55,6 @@ module_param(fnlock_default, bool, 0444);
|
|
#define to_asus_wmi_driver(pdrv) \
|
|
(container_of((pdrv), struct asus_wmi_driver, platform_driver))
|
|
|
|
-#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
|
|
-
|
|
#define NOTIFY_BRNUP_MIN 0x11
|
|
#define NOTIFY_BRNUP_MAX 0x1f
|
|
#define NOTIFY_BRNDOWN_MIN 0x20
|
|
@@ -105,8 +103,6 @@ module_param(fnlock_default, bool, 0444);
|
|
#define USB_INTEL_XUSB2PR 0xD0
|
|
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
|
|
|
|
-#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
|
|
-
|
|
#define WMI_EVENT_MASK 0xFFFF
|
|
|
|
#define FAN_CURVE_POINTS 8
|
|
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
|
index 4e6de0c97a78..ff0e762b83a4 100644
|
|
--- a/include/linux/platform_data/x86/asus-wmi.h
|
|
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
|
@@ -144,6 +144,9 @@ struct asus_wmi {
|
|
struct asus_wmi_driver *driver;
|
|
};
|
|
|
|
+#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
|
|
+#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
|
|
+
|
|
/* WMI Methods */
|
|
#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
|
|
#define ASUS_WMI_METHODID_SFBD 0x44424653 /* Set First Boot Device */
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 291c0678506e20bdeef67949b18a24c820c13697 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Thu, 19 Sep 2024 17:23:35 +1200
|
|
Subject: [PATCH 21/28] platform/x86: asus-armoury: add panel_hd_mode attribute
|
|
|
|
Add panel_hd_mode to toggle the panel mode between single and high
|
|
definition modes.
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
|
|
---
|
|
drivers/platform/x86/asus-armoury.c | 6 +++++-
|
|
include/linux/platform_data/x86/asus-wmi.h | 1 +
|
|
2 files changed, 6 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
|
|
index d2e8c21d62dc..716bf96b6b58 100644
|
|
--- a/drivers/platform/x86/asus-armoury.c
|
|
+++ b/drivers/platform/x86/asus-armoury.c
|
|
@@ -93,7 +93,8 @@ static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
|
|
|
|
static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
|
|
{
|
|
- return !strcmp(attr->attr.name, "gpu_mux_mode");
|
|
+ return !strcmp(attr->attr.name, "gpu_mux_mode") ||
|
|
+ !strcmp(attr->attr.name, "panel_hd_mode");
|
|
}
|
|
|
|
static int armoury_wmi_set_devstate(struct kobj_attribute *attr, u32 value, u32 wmi_dev)
|
|
@@ -405,6 +406,8 @@ ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE,
|
|
"Set MCU powersaving mode");
|
|
ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD,
|
|
"Set the panel refresh overdrive");
|
|
+ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD,
|
|
+ "Set the panel HD mode to UHD<0> or FHD<1>");
|
|
ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
|
|
"Show the eGPU connection status");
|
|
|
|
@@ -418,6 +421,7 @@ static const struct asus_attr_group armoury_attr_groups[] = {
|
|
{ &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND },
|
|
{ &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE },
|
|
{ &panel_od_attr_group, ASUS_WMI_DEVID_PANEL_OD },
|
|
+ { &panel_hd_mode_attr_group, ASUS_WMI_DEVID_PANEL_HD },
|
|
};
|
|
|
|
static int asus_fw_attr_add(void)
|
|
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
|
index ff0e762b83a4..28d9ec1326ab 100644
|
|
--- a/include/linux/platform_data/x86/asus-wmi.h
|
|
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
|
@@ -214,6 +214,7 @@ struct asus_wmi {
|
|
#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO 0x00110019
|
|
|
|
/* Misc */
|
|
+#define ASUS_WMI_DEVID_PANEL_HD 0x0005001C
|
|
#define ASUS_WMI_DEVID_PANEL_OD 0x00050019
|
|
#define ASUS_WMI_DEVID_CAMERA 0x00060013
|
|
#define ASUS_WMI_DEVID_LID_FLIP 0x00060062
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 6db68b4041e9a6b74d071f9f5088148916964fcc Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Sun, 2 Jun 2024 14:44:31 +1200
|
|
Subject: [PATCH 22/28] platform/x86: asus-armoury: add apu-mem control support
|
|
|
|
Implement the APU memory size control under the asus-armoury module using
|
|
the fw_attributes class.
|
|
|
|
This allows the APU allocated memory size to be adjusted depending on
|
|
the users priority. A reboot is required after change.
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
|
|
---
|
|
drivers/platform/x86/asus-armoury.c | 114 +++++++++++++++++++++
|
|
include/linux/platform_data/x86/asus-wmi.h | 2 +
|
|
2 files changed, 116 insertions(+)
|
|
|
|
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
|
|
index 716bf96b6b58..298f7b203d7c 100644
|
|
--- a/drivers/platform/x86/asus-armoury.c
|
|
+++ b/drivers/platform/x86/asus-armoury.c
|
|
@@ -396,6 +396,119 @@ static ssize_t egpu_enable_current_value_store(struct kobject *kobj, struct kobj
|
|
WMI_SHOW_INT(egpu_enable_current_value, "%d\n", ASUS_WMI_DEVID_EGPU);
|
|
ATTR_GROUP_BOOL_CUSTOM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)");
|
|
|
|
+/* Device memory available to APU */
|
|
+
|
|
+static ssize_t apu_mem_current_value_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ int err;
|
|
+ u32 mem;
|
|
+
|
|
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_APU_MEM, &mem);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ switch (mem) {
|
|
+ case 0x100:
|
|
+ mem = 0;
|
|
+ break;
|
|
+ case 0x102:
|
|
+ mem = 1;
|
|
+ break;
|
|
+ case 0x103:
|
|
+ mem = 2;
|
|
+ break;
|
|
+ case 0x104:
|
|
+ mem = 3;
|
|
+ break;
|
|
+ case 0x105:
|
|
+ mem = 4;
|
|
+ break;
|
|
+ case 0x106:
|
|
+ /* This is out of order and looks wrong but is correct */
|
|
+ mem = 8;
|
|
+ break;
|
|
+ case 0x107:
|
|
+ mem = 5;
|
|
+ break;
|
|
+ case 0x108:
|
|
+ mem = 6;
|
|
+ break;
|
|
+ case 0x109:
|
|
+ mem = 7;
|
|
+ break;
|
|
+ default:
|
|
+ mem = 4;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return sysfs_emit(buf, "%u\n", mem);
|
|
+}
|
|
+
|
|
+static ssize_t apu_mem_current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int result, err;
|
|
+ u32 requested, mem;
|
|
+
|
|
+ result = kstrtou32(buf, 10, &requested);
|
|
+ if (result)
|
|
+ return result;
|
|
+
|
|
+ switch (requested) {
|
|
+ case 0:
|
|
+ mem = 0x000;
|
|
+ break;
|
|
+ case 1:
|
|
+ mem = 0x102;
|
|
+ break;
|
|
+ case 2:
|
|
+ mem = 0x103;
|
|
+ break;
|
|
+ case 3:
|
|
+ mem = 0x104;
|
|
+ break;
|
|
+ case 4:
|
|
+ mem = 0x105;
|
|
+ break;
|
|
+ case 5:
|
|
+ mem = 0x107;
|
|
+ break;
|
|
+ case 6:
|
|
+ mem = 0x108;
|
|
+ break;
|
|
+ case 7:
|
|
+ mem = 0x109;
|
|
+ break;
|
|
+ case 8:
|
|
+ /* This is out of order and looks wrong but is correct */
|
|
+ mem = 0x106;
|
|
+ break;
|
|
+ default:
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_APU_MEM, mem, &result);
|
|
+ if (err) {
|
|
+ pr_warn("Failed to set apu_mem: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ pr_info("APU memory changed to %uGB, reboot required\n", requested);
|
|
+ sysfs_notify(kobj, NULL, attr->attr.name);
|
|
+
|
|
+ asus_set_reboot_and_signal_event();
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t apu_mem_possible_values_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return sysfs_emit(buf, "0;1;2;3;4;5;6;7;8\n");
|
|
+}
|
|
+ATTR_GROUP_ENUM_CUSTOM(apu_mem, "apu_mem", "Set available system RAM (in GB) for the APU to use");
|
|
+
|
|
/* Simple attribute creation */
|
|
ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2",
|
|
"Show the current mode of charging");
|
|
@@ -416,6 +529,7 @@ static const struct asus_attr_group armoury_attr_groups[] = {
|
|
{ &egpu_connected_attr_group, ASUS_WMI_DEVID_EGPU_CONNECTED },
|
|
{ &egpu_enable_attr_group, ASUS_WMI_DEVID_EGPU },
|
|
{ &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU },
|
|
+ { &apu_mem_attr_group, ASUS_WMI_DEVID_APU_MEM },
|
|
|
|
{ &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE },
|
|
{ &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND },
|
|
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
|
index 28d9ec1326ab..ba397d7d0825 100644
|
|
--- a/include/linux/platform_data/x86/asus-wmi.h
|
|
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
|
@@ -275,6 +275,8 @@ struct asus_wmi {
|
|
/* dgpu on/off */
|
|
#define ASUS_WMI_DEVID_DGPU 0x00090020
|
|
|
|
+#define ASUS_WMI_DEVID_APU_MEM 0x000600C1
|
|
+
|
|
/* gpu mux switch, 0 = dGPU, 1 = Optimus */
|
|
#define ASUS_WMI_DEVID_GPU_MUX 0x00090016
|
|
#define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From c9c56bcc8fca365ed6a80f1012dbd29d8c3070f6 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Wed, 18 Sep 2024 21:19:12 +1200
|
|
Subject: [PATCH 23/28] platform/x86: asus-armoury: add core count control
|
|
|
|
Implement Intel core enablement under the asus-armoury module using the
|
|
fw_attributes class.
|
|
|
|
This allows users to enable or disable preformance or efficiency cores
|
|
depending on their requirements. After change a reboot is required.
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
|
|
---
|
|
drivers/platform/x86/asus-armoury.c | 236 +++++++++++++++++++++
|
|
drivers/platform/x86/asus-armoury.h | 34 +++
|
|
include/linux/platform_data/x86/asus-wmi.h | 5 +
|
|
3 files changed, 275 insertions(+)
|
|
|
|
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
|
|
index 298f7b203d7c..49e1788d8bba 100644
|
|
--- a/drivers/platform/x86/asus-armoury.c
|
|
+++ b/drivers/platform/x86/asus-armoury.c
|
|
@@ -40,10 +40,39 @@
|
|
#define ASUS_MINI_LED_2024_STRONG 0x01
|
|
#define ASUS_MINI_LED_2024_OFF 0x02
|
|
|
|
+#define ASUS_POWER_CORE_MASK GENMASK(15, 8)
|
|
+#define ASUS_PERF_CORE_MASK GENMASK(7, 0)
|
|
+
|
|
+enum cpu_core_type {
|
|
+ CPU_CORE_PERF = 0,
|
|
+ CPU_CORE_POWER,
|
|
+};
|
|
+
|
|
+enum cpu_core_value {
|
|
+ CPU_CORE_DEFAULT = 0,
|
|
+ CPU_CORE_MIN,
|
|
+ CPU_CORE_MAX,
|
|
+ CPU_CORE_CURRENT,
|
|
+};
|
|
+
|
|
+#define CPU_PERF_CORE_COUNT_MIN 4
|
|
+#define CPU_POWR_CORE_COUNT_MIN 0
|
|
+
|
|
+/* Tunables provided by ASUS for gaming laptops */
|
|
+struct cpu_cores {
|
|
+ u32 cur_perf_cores;
|
|
+ u32 min_perf_cores;
|
|
+ u32 max_perf_cores;
|
|
+ u32 cur_power_cores;
|
|
+ u32 min_power_cores;
|
|
+ u32 max_power_cores;
|
|
+};
|
|
+
|
|
struct asus_armoury_priv {
|
|
struct device *fw_attr_dev;
|
|
struct kset *fw_attr_kset;
|
|
|
|
+ struct cpu_cores *cpu_cores;
|
|
u32 mini_led_dev_id;
|
|
u32 gpu_mux_dev_id;
|
|
|
|
@@ -94,6 +123,8 @@ static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
|
|
static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
|
|
{
|
|
return !strcmp(attr->attr.name, "gpu_mux_mode") ||
|
|
+ !strcmp(attr->attr.name, "cores_performance") ||
|
|
+ !strcmp(attr->attr.name, "cores_efficiency") ||
|
|
!strcmp(attr->attr.name, "panel_hd_mode");
|
|
}
|
|
|
|
@@ -509,6 +540,195 @@ static ssize_t apu_mem_possible_values_show(struct kobject *kobj, struct kobj_at
|
|
}
|
|
ATTR_GROUP_ENUM_CUSTOM(apu_mem, "apu_mem", "Set available system RAM (in GB) for the APU to use");
|
|
|
|
+static int init_max_cpu_cores(void)
|
|
+{
|
|
+ u32 cores;
|
|
+ int err;
|
|
+
|
|
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES_MAX, &cores);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
|
|
+ asus_armoury.cpu_cores->max_power_cores = FIELD_GET(ASUS_POWER_CORE_MASK, cores);
|
|
+ asus_armoury.cpu_cores->max_perf_cores = FIELD_GET(ASUS_PERF_CORE_MASK, cores);
|
|
+
|
|
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, &cores);
|
|
+ if (err) {
|
|
+ pr_err("Could not get CPU core count: error %d", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ asus_armoury.cpu_cores->cur_perf_cores = FIELD_GET(ASUS_PERF_CORE_MASK, cores);
|
|
+ asus_armoury.cpu_cores->cur_power_cores = FIELD_GET(ASUS_POWER_CORE_MASK, cores);
|
|
+
|
|
+ asus_armoury.cpu_cores->min_perf_cores = CPU_PERF_CORE_COUNT_MIN;
|
|
+ asus_armoury.cpu_cores->min_power_cores = CPU_POWR_CORE_COUNT_MIN;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t cores_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf,
|
|
+ enum cpu_core_type core_type, enum cpu_core_value core_value)
|
|
+{
|
|
+ u32 cores;
|
|
+
|
|
+ switch (core_value) {
|
|
+ case CPU_CORE_DEFAULT:
|
|
+ case CPU_CORE_MAX:
|
|
+ if (core_type == CPU_CORE_PERF)
|
|
+ return sysfs_emit(buf, "%d\n",
|
|
+ asus_armoury.cpu_cores->max_perf_cores);
|
|
+ else
|
|
+ return sysfs_emit(buf, "%d\n",
|
|
+ asus_armoury.cpu_cores->max_power_cores);
|
|
+ case CPU_CORE_MIN:
|
|
+ if (core_type == CPU_CORE_PERF)
|
|
+ return sysfs_emit(buf, "%d\n",
|
|
+ asus_armoury.cpu_cores->min_perf_cores);
|
|
+ else
|
|
+ return sysfs_emit(buf, "%d\n",
|
|
+ asus_armoury.cpu_cores->min_power_cores);
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (core_type == CPU_CORE_PERF)
|
|
+ cores = asus_armoury.cpu_cores->cur_perf_cores;
|
|
+ else
|
|
+ cores = asus_armoury.cpu_cores->cur_power_cores;
|
|
+
|
|
+ return sysfs_emit(buf, "%d\n", cores);
|
|
+}
|
|
+
|
|
+static ssize_t cores_current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ const char *buf, enum cpu_core_type core_type)
|
|
+{
|
|
+ u32 new_cores, perf_cores, power_cores, out_val, min, max;
|
|
+ int result, err;
|
|
+
|
|
+ result = kstrtou32(buf, 10, &new_cores);
|
|
+ if (result)
|
|
+ return result;
|
|
+
|
|
+ if (core_type == CPU_CORE_PERF) {
|
|
+ perf_cores = new_cores;
|
|
+ power_cores = out_val = asus_armoury.cpu_cores->cur_power_cores;
|
|
+ min = asus_armoury.cpu_cores->min_perf_cores;
|
|
+ max = asus_armoury.cpu_cores->max_perf_cores;
|
|
+ } else {
|
|
+ perf_cores = asus_armoury.cpu_cores->cur_perf_cores;
|
|
+ power_cores = out_val = new_cores;
|
|
+ min = asus_armoury.cpu_cores->min_power_cores;
|
|
+ max = asus_armoury.cpu_cores->max_power_cores;
|
|
+ }
|
|
+
|
|
+ if (new_cores < min || new_cores > max)
|
|
+ return -EINVAL;
|
|
+
|
|
+ out_val = 0;
|
|
+ out_val |= FIELD_PREP(ASUS_PERF_CORE_MASK, perf_cores);
|
|
+ out_val |= FIELD_PREP(ASUS_POWER_CORE_MASK, power_cores);
|
|
+
|
|
+ mutex_lock(&asus_armoury.mutex);
|
|
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_CORES, out_val, &result);
|
|
+ mutex_unlock(&asus_armoury.mutex);
|
|
+
|
|
+ if (err) {
|
|
+ pr_warn("Failed to set CPU core count: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (result > 1) {
|
|
+ pr_warn("Failed to set CPU core count (result): 0x%x\n", result);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ pr_info("CPU core count changed, reboot required\n");
|
|
+ sysfs_notify(kobj, NULL, attr->attr.name);
|
|
+ asus_set_reboot_and_signal_event();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t cores_performance_min_value_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MIN);
|
|
+}
|
|
+
|
|
+static ssize_t cores_performance_max_value_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MAX);
|
|
+}
|
|
+
|
|
+static ssize_t cores_performance_default_value_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_DEFAULT);
|
|
+}
|
|
+
|
|
+static ssize_t cores_performance_current_value_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_CURRENT);
|
|
+}
|
|
+
|
|
+static ssize_t cores_performance_current_value_store(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ err = cores_current_value_store(kobj, attr, buf, CPU_CORE_PERF);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+ATTR_GROUP_CORES_RW(cores_performance, "cores_performance",
|
|
+ "Set the max available performance cores");
|
|
+
|
|
+static ssize_t cores_efficiency_min_value_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MIN);
|
|
+}
|
|
+
|
|
+static ssize_t cores_efficiency_max_value_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MAX);
|
|
+}
|
|
+
|
|
+static ssize_t cores_efficiency_default_value_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_DEFAULT);
|
|
+}
|
|
+
|
|
+static ssize_t cores_efficiency_current_value_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, char *buf)
|
|
+{
|
|
+ return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_CURRENT);
|
|
+}
|
|
+
|
|
+static ssize_t cores_efficiency_current_value_store(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr, const char *buf,
|
|
+ size_t count)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ err = cores_current_value_store(kobj, attr, buf, CPU_CORE_POWER);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+ATTR_GROUP_CORES_RW(cores_efficiency, "cores_efficiency",
|
|
+ "Set the max available efficiency cores");
|
|
+
|
|
/* Simple attribute creation */
|
|
ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2",
|
|
"Show the current mode of charging");
|
|
@@ -530,6 +750,8 @@ static const struct asus_attr_group armoury_attr_groups[] = {
|
|
{ &egpu_enable_attr_group, ASUS_WMI_DEVID_EGPU },
|
|
{ &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU },
|
|
{ &apu_mem_attr_group, ASUS_WMI_DEVID_APU_MEM },
|
|
+ { &cores_efficiency_attr_group, ASUS_WMI_DEVID_CORES_MAX },
|
|
+ { &cores_performance_attr_group, ASUS_WMI_DEVID_CORES_MAX },
|
|
|
|
{ &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE },
|
|
{ &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND },
|
|
@@ -643,6 +865,20 @@ static int __init asus_fw_init(void)
|
|
if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI))
|
|
return -ENODEV;
|
|
|
|
+
|
|
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_CORES_MAX)) {
|
|
+ asus_armoury.cpu_cores = kzalloc(sizeof(struct cpu_cores), GFP_KERNEL);
|
|
+ if (!asus_armoury.cpu_cores)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ err = init_max_cpu_cores();
|
|
+ if (err) {
|
|
+ kfree(asus_armoury.cpu_cores);
|
|
+ pr_err("Could not initialise CPU core control %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+ }
|
|
+
|
|
err = asus_fw_attr_add();
|
|
if (err)
|
|
return err;
|
|
diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h
|
|
index 440f41c5df3b..5d6bef6d2b12 100644
|
|
--- a/drivers/platform/x86/asus-armoury.h
|
|
+++ b/drivers/platform/x86/asus-armoury.h
|
|
@@ -23,6 +23,12 @@ static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
return sysfs_emit(buf, "enumeration\n");
|
|
}
|
|
|
|
+static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return sysfs_emit(buf, "integer\n");
|
|
+}
|
|
+
|
|
#define __ASUS_ATTR_RO(_func, _name) \
|
|
{ \
|
|
.attr = { .name = __stringify(_name), .mode = 0444 }, \
|
|
@@ -144,4 +150,32 @@ static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
.name = _fsname, .attrs = _attrname##_attrs \
|
|
}
|
|
|
|
+/* CPU core attributes need a little different in setup */
|
|
+#define ATTR_GROUP_CORES_RW(_attrname, _fsname, _dispname) \
|
|
+ __ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \
|
|
+ __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
|
|
+ static struct kobj_attribute attr_##_attrname##_current_value = \
|
|
+ __ASUS_ATTR_RW(_attrname, current_value); \
|
|
+ static struct kobj_attribute attr_##_attrname##_default_value = \
|
|
+ __ASUS_ATTR_RO(_attrname, default_value); \
|
|
+ static struct kobj_attribute attr_##_attrname##_min_value = \
|
|
+ __ASUS_ATTR_RO(_attrname, min_value); \
|
|
+ static struct kobj_attribute attr_##_attrname##_max_value = \
|
|
+ __ASUS_ATTR_RO(_attrname, max_value); \
|
|
+ static struct kobj_attribute attr_##_attrname##_type = \
|
|
+ __ASUS_ATTR_RO_AS(type, int_type_show); \
|
|
+ static struct attribute *_attrname##_attrs[] = { \
|
|
+ &attr_##_attrname##_current_value.attr, \
|
|
+ &attr_##_attrname##_default_value.attr, \
|
|
+ &attr_##_attrname##_min_value.attr, \
|
|
+ &attr_##_attrname##_max_value.attr, \
|
|
+ &attr_##_attrname##_scalar_increment.attr, \
|
|
+ &attr_##_attrname##_display_name.attr, \
|
|
+ &attr_##_attrname##_type.attr, \
|
|
+ NULL \
|
|
+ }; \
|
|
+ static const struct attribute_group _attrname##_attr_group = { \
|
|
+ .name = _fsname, .attrs = _attrname##_attrs \
|
|
+ }
|
|
+
|
|
#endif /* _ASUS_ARMOURY_H_ */
|
|
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
|
index ba397d7d0825..5af4430c863f 100644
|
|
--- a/include/linux/platform_data/x86/asus-wmi.h
|
|
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
|
@@ -275,6 +275,11 @@ struct asus_wmi {
|
|
/* dgpu on/off */
|
|
#define ASUS_WMI_DEVID_DGPU 0x00090020
|
|
|
|
+/* Intel E-core and P-core configuration in a format 0x0[E]0[P] */
|
|
+#define ASUS_WMI_DEVID_CORES 0x001200D2
|
|
+ /* Maximum Intel E-core and P-core availability */
|
|
+#define ASUS_WMI_DEVID_CORES_MAX 0x001200D3
|
|
+
|
|
#define ASUS_WMI_DEVID_APU_MEM 0x000600C1
|
|
|
|
/* gpu mux switch, 0 = dGPU, 1 = Optimus */
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 4be3d720922e9488adf138754fb9c9ba47247448 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Mon, 3 Jun 2024 12:04:41 +1200
|
|
Subject: [PATCH 24/28] platform/x86: asus-wmi: deprecate bios features
|
|
|
|
With the existence of the asus-armoury module the attributes no-longer
|
|
need to live under the /sys/devices/platform/asus-nb-wmi/ path.
|
|
|
|
Deprecate all those that were implemented in asus-bioscfg with the goal
|
|
of removing them fully in the next LTS cycle.
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
|
|
---
|
|
.../ABI/testing/sysfs-platform-asus-wmi | 17 +++
|
|
drivers/platform/x86/Kconfig | 9 ++
|
|
drivers/platform/x86/asus-wmi.c | 123 ++++++++++++++----
|
|
include/linux/platform_data/x86/asus-wmi.h | 17 ++-
|
|
4 files changed, 134 insertions(+), 32 deletions(-)
|
|
|
|
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
|
index 28144371a0f1..765d50b0d9df 100644
|
|
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
|
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
|
@@ -63,6 +63,7 @@ Date: Aug 2022
|
|
KernelVersion: 6.1
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
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:
|
|
@@ -75,6 +76,7 @@ Date: Aug 2022
|
|
KernelVersion: 5.17
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Disable discrete GPU:
|
|
* 0 - Enable dGPU,
|
|
* 1 - Disable dGPU
|
|
@@ -84,6 +86,7 @@ Date: Aug 2022
|
|
KernelVersion: 5.17
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Enable the external GPU paired with ROG X-Flow laptops.
|
|
Toggling this setting will also trigger ACPI to disable the dGPU:
|
|
|
|
@@ -95,6 +98,7 @@ Date: Aug 2022
|
|
KernelVersion: 5.17
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Enable an LCD response-time boost to reduce or remove ghosting:
|
|
* 0 - Disable,
|
|
* 1 - Enable
|
|
@@ -104,6 +108,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Get the current charging mode being used:
|
|
* 1 - Barrel connected charger,
|
|
* 2 - USB-C charging
|
|
@@ -114,6 +119,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Show if the egpu (XG Mobile) is correctly connected:
|
|
* 0 - False,
|
|
* 1 - True
|
|
@@ -123,6 +129,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Change the mini-LED mode:
|
|
* 0 - Single-zone,
|
|
* 1 - Multi-zone
|
|
@@ -133,6 +140,7 @@ Date: Apr 2024
|
|
KernelVersion: 6.10
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
List the available mini-led modes.
|
|
|
|
What: /sys/devices/platform/<platform>/ppt_pl1_spl
|
|
@@ -140,6 +148,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set the Package Power Target total of CPU: PL1 on Intel, SPL on AMD.
|
|
Shown on Intel+Nvidia or AMD+Nvidia based systems:
|
|
|
|
@@ -150,6 +159,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set the Slow Package Power Tracking Limit of CPU: PL2 on Intel, SPPT,
|
|
on AMD. Shown on Intel+Nvidia or AMD+Nvidia based systems:
|
|
|
|
@@ -160,6 +170,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set the Fast Package Power Tracking Limit of CPU. AMD+Nvidia only:
|
|
* min=5, max=250
|
|
|
|
@@ -168,6 +179,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set the APU SPPT limit. Shown on full AMD systems only:
|
|
* min=5, max=130
|
|
|
|
@@ -176,6 +188,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set the platform SPPT limit. Shown on full AMD systems only:
|
|
* min=5, max=130
|
|
|
|
@@ -184,6 +197,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set the dynamic boost limit of the Nvidia dGPU:
|
|
* min=5, max=25
|
|
|
|
@@ -192,6 +206,7 @@ Date: Jun 2023
|
|
KernelVersion: 6.5
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set the target temperature limit of the Nvidia dGPU:
|
|
* min=75, max=87
|
|
|
|
@@ -200,6 +215,7 @@ Date: Apr 2024
|
|
KernelVersion: 6.10
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set if the BIOS POST sound is played on boot.
|
|
* 0 - False,
|
|
* 1 - True
|
|
@@ -209,6 +225,7 @@ Date: Apr 2024
|
|
KernelVersion: 6.10
|
|
Contact: "Luke Jones" <luke@ljones.dev>
|
|
Description:
|
|
+ DEPRECATED, WILL BE REMOVED SOON
|
|
Set if the MCU can go in to low-power mode on system sleep
|
|
* 0 - False,
|
|
* 1 - True
|
|
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
|
|
index 294364cc7478..b160173a530e 100644
|
|
--- a/drivers/platform/x86/Kconfig
|
|
+++ b/drivers/platform/x86/Kconfig
|
|
@@ -301,6 +301,15 @@ config ASUS_WMI
|
|
To compile this driver as a module, choose M here: the module will
|
|
be called asus-wmi.
|
|
|
|
+config ASUS_WMI_DEPRECATED_ATTRS
|
|
+ bool "BIOS option support in WMI platform (DEPRECATED)"
|
|
+ depends on ASUS_WMI
|
|
+ default y
|
|
+ help
|
|
+ Say Y to expose the configurable BIOS options through the asus-wmi
|
|
+ driver. This can be used with or without the asus-armoury driver which
|
|
+ has the same attributes, but more, and better features.
|
|
+
|
|
config ASUS_NB_WMI
|
|
tristate "Asus Notebook WMI Driver"
|
|
depends on ASUS_WMI
|
|
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
|
index b91cb5538b2d..7e9878851f40 100644
|
|
--- a/drivers/platform/x86/asus-wmi.c
|
|
+++ b/drivers/platform/x86/asus-wmi.c
|
|
@@ -194,6 +194,15 @@ struct agfn_fan_args {
|
|
u32 speed; /* read: RPM/100 - write: 0-255 */
|
|
} __packed;
|
|
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
+static void asus_wmi_show_deprecated(void)
|
|
+{
|
|
+ pr_notice_once("Accessing attributes through /sys/bus/platform/asus_wmi "
|
|
+ "is deprecated and will be removed in a future release. Please "
|
|
+ "switch over to /sys/class/firmware_attributes.\n");
|
|
+}
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
+
|
|
/* WMI ************************************************************************/
|
|
|
|
static int asus_wmi_evaluate_method3(u32 method_id,
|
|
@@ -584,6 +593,7 @@ static void asus_wmi_tablet_mode_get_state(struct asus_wmi *asus)
|
|
}
|
|
|
|
/* Charging mode, 1=Barrel, 2=USB ******************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t charge_mode_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -594,12 +604,16 @@ static ssize_t charge_mode_show(struct device *dev,
|
|
if (result < 0)
|
|
return result;
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", value & 0xff);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(charge_mode);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* dGPU ********************************************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t dgpu_disable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -610,6 +624,8 @@ static ssize_t dgpu_disable_show(struct device *dev,
|
|
if (result < 0)
|
|
return result;
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", result);
|
|
}
|
|
|
|
@@ -663,8 +679,10 @@ static ssize_t dgpu_disable_store(struct device *dev,
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR_RW(dgpu_disable);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* eGPU ********************************************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t egpu_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -675,6 +693,8 @@ static ssize_t egpu_enable_show(struct device *dev,
|
|
if (result < 0)
|
|
return result;
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", result);
|
|
}
|
|
|
|
@@ -731,8 +751,10 @@ static ssize_t egpu_enable_store(struct device *dev,
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR_RW(egpu_enable);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* Is eGPU connected? *********************************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t egpu_connected_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -743,12 +765,16 @@ static ssize_t egpu_connected_show(struct device *dev,
|
|
if (result < 0)
|
|
return result;
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", result);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(egpu_connected);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* gpu mux switch *************************************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t gpu_mux_mode_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -759,6 +785,8 @@ static ssize_t gpu_mux_mode_show(struct device *dev,
|
|
if (result < 0)
|
|
return result;
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", result);
|
|
}
|
|
|
|
@@ -817,6 +845,7 @@ static ssize_t gpu_mux_mode_store(struct device *dev,
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR_RW(gpu_mux_mode);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* TUF Laptop Keyboard RGB Modes **********************************************/
|
|
static ssize_t kbd_rgb_mode_store(struct device *dev,
|
|
@@ -940,6 +969,7 @@ static const struct attribute_group *kbd_rgb_mode_groups[] = {
|
|
};
|
|
|
|
/* Tunable: PPT: Intel=PL1, AMD=SPPT *****************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t ppt_pl2_sppt_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
@@ -978,6 +1008,8 @@ static ssize_t ppt_pl2_sppt_show(struct device *dev,
|
|
{
|
|
struct asus_wmi *asus = dev_get_drvdata(dev);
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%u\n", asus->ppt_pl2_sppt);
|
|
}
|
|
static DEVICE_ATTR_RW(ppt_pl2_sppt);
|
|
@@ -1020,6 +1052,8 @@ static ssize_t ppt_pl1_spl_show(struct device *dev,
|
|
{
|
|
struct asus_wmi *asus = dev_get_drvdata(dev);
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%u\n", asus->ppt_pl1_spl);
|
|
}
|
|
static DEVICE_ATTR_RW(ppt_pl1_spl);
|
|
@@ -1063,6 +1097,8 @@ static ssize_t ppt_fppt_show(struct device *dev,
|
|
{
|
|
struct asus_wmi *asus = dev_get_drvdata(dev);
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%u\n", asus->ppt_fppt);
|
|
}
|
|
static DEVICE_ATTR_RW(ppt_fppt);
|
|
@@ -1106,6 +1142,8 @@ static ssize_t ppt_apu_sppt_show(struct device *dev,
|
|
{
|
|
struct asus_wmi *asus = dev_get_drvdata(dev);
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%u\n", asus->ppt_apu_sppt);
|
|
}
|
|
static DEVICE_ATTR_RW(ppt_apu_sppt);
|
|
@@ -1149,6 +1187,8 @@ static ssize_t ppt_platform_sppt_show(struct device *dev,
|
|
{
|
|
struct asus_wmi *asus = dev_get_drvdata(dev);
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%u\n", asus->ppt_platform_sppt);
|
|
}
|
|
static DEVICE_ATTR_RW(ppt_platform_sppt);
|
|
@@ -1192,6 +1232,8 @@ static ssize_t nv_dynamic_boost_show(struct device *dev,
|
|
{
|
|
struct asus_wmi *asus = dev_get_drvdata(dev);
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%u\n", asus->nv_dynamic_boost);
|
|
}
|
|
static DEVICE_ATTR_RW(nv_dynamic_boost);
|
|
@@ -1235,11 +1277,15 @@ static ssize_t nv_temp_target_show(struct device *dev,
|
|
{
|
|
struct asus_wmi *asus = dev_get_drvdata(dev);
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%u\n", asus->nv_temp_target);
|
|
}
|
|
static DEVICE_ATTR_RW(nv_temp_target);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* Ally MCU Powersave ********************************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t mcu_powersave_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -1250,6 +1296,8 @@ static ssize_t mcu_powersave_show(struct device *dev,
|
|
if (result < 0)
|
|
return result;
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", result);
|
|
}
|
|
|
|
@@ -1285,6 +1333,7 @@ static ssize_t mcu_powersave_store(struct device *dev,
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR_RW(mcu_powersave);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* Battery ********************************************************************/
|
|
|
|
@@ -2158,6 +2207,7 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
|
|
}
|
|
|
|
/* Panel Overdrive ************************************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t panel_od_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -2168,6 +2218,8 @@ static ssize_t panel_od_show(struct device *dev,
|
|
if (result < 0)
|
|
return result;
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", result);
|
|
}
|
|
|
|
@@ -2204,9 +2256,10 @@ static ssize_t panel_od_store(struct device *dev,
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR_RW(panel_od);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* Bootup sound ***************************************************************/
|
|
-
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t boot_sound_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -2217,6 +2270,8 @@ static ssize_t boot_sound_show(struct device *dev,
|
|
if (result < 0)
|
|
return result;
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", result);
|
|
}
|
|
|
|
@@ -2252,8 +2307,10 @@ static ssize_t boot_sound_store(struct device *dev,
|
|
return count;
|
|
}
|
|
static DEVICE_ATTR_RW(boot_sound);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* Mini-LED mode **************************************************************/
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t mini_led_mode_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -2284,6 +2341,8 @@ static ssize_t mini_led_mode_show(struct device *dev,
|
|
}
|
|
}
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "%d\n", value);
|
|
}
|
|
|
|
@@ -2354,10 +2413,13 @@ static ssize_t available_mini_led_mode_show(struct device *dev,
|
|
return sysfs_emit(buf, "0 1 2\n");
|
|
}
|
|
|
|
+ asus_wmi_show_deprecated();
|
|
+
|
|
return sysfs_emit(buf, "0\n");
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(available_mini_led_mode);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* Quirks *********************************************************************/
|
|
|
|
@@ -3645,6 +3707,7 @@ static int throttle_thermal_policy_set_default(struct asus_wmi *asus)
|
|
return throttle_thermal_policy_write(asus);
|
|
}
|
|
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
static ssize_t throttle_thermal_policy_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
@@ -3688,6 +3751,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
|
|
* Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
|
|
*/
|
|
static DEVICE_ATTR_RW(throttle_thermal_policy);
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
/* Platform profile ***********************************************************/
|
|
static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
|
@@ -4285,27 +4349,29 @@ static struct attribute *platform_attributes[] = {
|
|
&dev_attr_camera.attr,
|
|
&dev_attr_cardr.attr,
|
|
&dev_attr_touchpad.attr,
|
|
- &dev_attr_charge_mode.attr,
|
|
- &dev_attr_egpu_enable.attr,
|
|
- &dev_attr_egpu_connected.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,
|
|
- &dev_attr_throttle_thermal_policy.attr,
|
|
- &dev_attr_ppt_pl2_sppt.attr,
|
|
- &dev_attr_ppt_pl1_spl.attr,
|
|
- &dev_attr_ppt_fppt.attr,
|
|
- &dev_attr_ppt_apu_sppt.attr,
|
|
- &dev_attr_ppt_platform_sppt.attr,
|
|
- &dev_attr_nv_dynamic_boost.attr,
|
|
- &dev_attr_nv_temp_target.attr,
|
|
- &dev_attr_mcu_powersave.attr,
|
|
- &dev_attr_boot_sound.attr,
|
|
- &dev_attr_panel_od.attr,
|
|
- &dev_attr_mini_led_mode.attr,
|
|
- &dev_attr_available_mini_led_mode.attr,
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
+ &dev_attr_charge_mode.attr,
|
|
+ &dev_attr_egpu_enable.attr,
|
|
+ &dev_attr_egpu_connected.attr,
|
|
+ &dev_attr_dgpu_disable.attr,
|
|
+ &dev_attr_gpu_mux_mode.attr,
|
|
+ &dev_attr_ppt_pl2_sppt.attr,
|
|
+ &dev_attr_ppt_pl1_spl.attr,
|
|
+ &dev_attr_ppt_fppt.attr,
|
|
+ &dev_attr_ppt_apu_sppt.attr,
|
|
+ &dev_attr_ppt_platform_sppt.attr,
|
|
+ &dev_attr_nv_dynamic_boost.attr,
|
|
+ &dev_attr_nv_temp_target.attr,
|
|
+ &dev_attr_mcu_powersave.attr,
|
|
+ &dev_attr_boot_sound.attr,
|
|
+ &dev_attr_panel_od.attr,
|
|
+ &dev_attr_mini_led_mode.attr,
|
|
+ &dev_attr_available_mini_led_mode.attr,
|
|
+ &dev_attr_throttle_thermal_policy.attr,
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
NULL
|
|
};
|
|
|
|
@@ -4327,7 +4393,11 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
|
devid = ASUS_WMI_DEVID_LID_RESUME;
|
|
else if (attr == &dev_attr_als_enable.attr)
|
|
devid = ASUS_WMI_DEVID_ALS_ENABLE;
|
|
- else if (attr == &dev_attr_charge_mode.attr)
|
|
+ else if (attr == &dev_attr_fan_boost_mode.attr)
|
|
+ ok = asus->fan_boost_mode_available;
|
|
+
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
+ if (attr == &dev_attr_charge_mode.attr)
|
|
devid = ASUS_WMI_DEVID_CHARGE_MODE;
|
|
else if (attr == &dev_attr_egpu_enable.attr)
|
|
ok = asus->egpu_enable_available;
|
|
@@ -4365,6 +4435,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
|
ok = asus->mini_led_dev_id != 0;
|
|
else if (attr == &dev_attr_available_mini_led_mode.attr)
|
|
ok = asus->mini_led_dev_id != 0;
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
if (devid != -1) {
|
|
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
|
|
@@ -4605,6 +4676,7 @@ static int asus_wmi_add(struct platform_device *pdev)
|
|
goto fail_platform;
|
|
|
|
/* ensure defaults for tunables */
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
asus->ppt_pl2_sppt = 5;
|
|
asus->ppt_pl1_spl = 5;
|
|
asus->ppt_apu_sppt = 5;
|
|
@@ -4627,17 +4699,18 @@ static int asus_wmi_add(struct platform_device *pdev)
|
|
asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX;
|
|
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX_VIVO))
|
|
asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX_VIVO;
|
|
-
|
|
- if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE))
|
|
- asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE;
|
|
- else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2))
|
|
- asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2;
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
|
|
asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY;
|
|
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO))
|
|
asus->throttle_thermal_policy_dev = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO;
|
|
|
|
+ if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE))
|
|
+ asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE;
|
|
+ else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2))
|
|
+ asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2;
|
|
+
|
|
err = fan_boost_mode_check_present(asus);
|
|
if (err)
|
|
goto fail_fan_boost_mode;
|
|
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
|
index 5af4430c863f..3ba0f8fef150 100644
|
|
--- a/include/linux/platform_data/x86/asus-wmi.h
|
|
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
|
@@ -10,6 +10,8 @@
|
|
#include <linux/pci_hotplug.h>
|
|
#include <linux/platform_profile.h>
|
|
|
|
+#define FAN_CURVE_POINTS 8
|
|
+
|
|
/*
|
|
* <platform>/ - debugfs root directory
|
|
* dev_id - current dev_id
|
|
@@ -32,14 +34,14 @@ struct asus_rfkill {
|
|
u32 dev_id;
|
|
};
|
|
|
|
+#define FAN_CURVE_POINTS 8
|
|
+
|
|
enum fan_type {
|
|
FAN_TYPE_NONE = 0,
|
|
FAN_TYPE_AGFN, /* deprecated on newer platforms */
|
|
FAN_TYPE_SPEC83, /* starting in Spec 8.3, use CPU_FAN_CTRL */
|
|
};
|
|
|
|
-#define FAN_CURVE_POINTS 8
|
|
-
|
|
struct fan_curve_data {
|
|
bool enabled;
|
|
u32 device_id;
|
|
@@ -97,11 +99,12 @@ struct asus_wmi {
|
|
u8 fan_boost_mode_mask;
|
|
u8 fan_boost_mode;
|
|
|
|
+
|
|
+ /* Tunables provided by ASUS for gaming laptops */
|
|
+#if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
|
|
bool egpu_enable_available;
|
|
bool dgpu_disable_available;
|
|
u32 gpu_mux_dev;
|
|
-
|
|
- /* Tunables provided by ASUS for gaming laptops */
|
|
u32 ppt_pl2_sppt;
|
|
u32 ppt_pl1_spl;
|
|
u32 ppt_apu_sppt;
|
|
@@ -109,6 +112,9 @@ struct asus_wmi {
|
|
u32 ppt_fppt;
|
|
u32 nv_dynamic_boost;
|
|
u32 nv_temp_target;
|
|
+ bool panel_overdrive_available;
|
|
+ u32 mini_led_dev_id;
|
|
+#endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
|
|
|
|
u32 kbd_rgb_dev;
|
|
bool kbd_rgb_state_available;
|
|
@@ -128,9 +134,6 @@ struct asus_wmi {
|
|
// The RSOC controls the maximum charging percentage.
|
|
bool battery_rsoc_available;
|
|
|
|
- bool panel_overdrive_available;
|
|
- u32 mini_led_dev_id;
|
|
-
|
|
struct hotplug_slot hotplug_slot;
|
|
struct mutex hotplug_lock;
|
|
struct mutex wmi_lock;
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From a874558b5b9953e216c0f17582cd6c91f4214840 Mon Sep 17 00:00:00 2001
|
|
From: Mario Limonciello <mario.limonciello@amd.com>
|
|
Date: Tue, 21 Jan 2025 16:03:52 -0600
|
|
Subject: [PATCH 25/28] drm/amd/display: Avoid divide by zero by initializing
|
|
dummy pitch to 1
|
|
|
|
If the dummy values in `populate_dummy_dml_surface_cfg()` aren't updated
|
|
then they can lead to a divide by zero in downstream callers like
|
|
CalculateVMAndRowBytes()
|
|
|
|
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
|
|
---
|
|
drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c
|
|
index bde4250853b1..f07afe451006 100644
|
|
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c
|
|
+++ b/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c
|
|
@@ -852,7 +852,7 @@ static void populate_dummy_dml_surface_cfg(struct dml_surface_cfg_st *out, unsig
|
|
out->SurfaceWidthC[location] = in->timing.h_addressable;
|
|
out->SurfaceHeightC[location] = in->timing.v_addressable;
|
|
out->PitchY[location] = ((out->SurfaceWidthY[location] + 127) / 128) * 128;
|
|
- out->PitchC[location] = 0;
|
|
+ out->PitchC[location] = 1;
|
|
out->DCCEnable[location] = false;
|
|
out->DCCMetaPitchY[location] = 0;
|
|
out->DCCMetaPitchC[location] = 0;
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 4f6e377bcec0ed811d32a96098b7adf69f72fa51 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Thu, 19 Sep 2024 17:19:37 +1200
|
|
Subject: [PATCH 26/28] platform/x86: asus-armoury: add the ppt_* and nv_*
|
|
tuning knobs
|
|
|
|
Adds the ppt_* and nv_* tuning knobs that are available via WMI methods
|
|
and adds proper min/max levels plus defaults.
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/platform/x86/asus-armoury.c | 182 ++++-
|
|
drivers/platform/x86/asus-armoury.h | 778 ++++++++++++++++++++-
|
|
include/linux/platform_data/x86/asus-wmi.h | 3 +
|
|
3 files changed, 950 insertions(+), 13 deletions(-)
|
|
|
|
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
|
|
index 49e1788d8bba..5768997ea2d3 100644
|
|
--- a/drivers/platform/x86/asus-armoury.c
|
|
+++ b/drivers/platform/x86/asus-armoury.c
|
|
@@ -21,6 +21,7 @@
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/platform_data/x86/asus-wmi.h>
|
|
+#include <linux/power_supply.h>
|
|
#include <linux/types.h>
|
|
#include <linux/acpi.h>
|
|
|
|
@@ -68,11 +69,25 @@ struct cpu_cores {
|
|
u32 max_power_cores;
|
|
};
|
|
|
|
+struct rog_tunables {
|
|
+ const struct power_data *tuning_limits;
|
|
+ u32 ppt_pl1_spl; // cpu
|
|
+ u32 ppt_pl2_sppt; // cpu
|
|
+ u32 ppt_pl3_fppt; // cpu
|
|
+ u32 ppt_apu_sppt; // plat
|
|
+ u32 ppt_platform_sppt; // plat
|
|
+
|
|
+ u32 nv_dynamic_boost;
|
|
+ u32 nv_temp_target;
|
|
+ u32 nv_tgp;
|
|
+};
|
|
+
|
|
struct asus_armoury_priv {
|
|
struct device *fw_attr_dev;
|
|
struct kset *fw_attr_kset;
|
|
|
|
struct cpu_cores *cpu_cores;
|
|
+ struct rog_tunables *rog_tunables;
|
|
u32 mini_led_dev_id;
|
|
u32 gpu_mux_dev_id;
|
|
|
|
@@ -730,6 +745,26 @@ ATTR_GROUP_CORES_RW(cores_efficiency, "cores_efficiency",
|
|
"Set the max available efficiency cores");
|
|
|
|
/* Simple attribute creation */
|
|
+ATTR_GROUP_ROG_TUNABLE(ppt_pl1_spl, "ppt_pl1_spl", ASUS_WMI_DEVID_PPT_PL1_SPL,
|
|
+ "Set the CPU slow package limit");
|
|
+ATTR_GROUP_ROG_TUNABLE(ppt_pl2_sppt, "ppt_pl2_sppt", ASUS_WMI_DEVID_PPT_PL2_SPPT,
|
|
+ "Set the CPU fast package limit");
|
|
+ATTR_GROUP_ROG_TUNABLE(ppt_pl3_fppt, "ppt_pl3_fppt", ASUS_WMI_DEVID_PPT_FPPT,
|
|
+ "Set the CPU fastest package limit");
|
|
+ATTR_GROUP_ROG_TUNABLE(ppt_apu_sppt, "ppt_apu_sppt", ASUS_WMI_DEVID_PPT_APU_SPPT,
|
|
+ "Set the APU package limit");
|
|
+ATTR_GROUP_ROG_TUNABLE(ppt_platform_sppt, "ppt_platform_sppt", ASUS_WMI_DEVID_PPT_PLAT_SPPT,
|
|
+ "Set the platform package limit");
|
|
+ATTR_GROUP_ROG_TUNABLE(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BOOST,
|
|
+ "Set the Nvidia dynamic boost limit");
|
|
+ATTR_GROUP_ROG_TUNABLE(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
|
|
+ "Set the Nvidia max thermal limit");
|
|
+ATTR_GROUP_ROG_TUNABLE(nv_tgp, "dgpu_tgp", ASUS_WMI_DEVID_DGPU_SET_TGP,
|
|
+ "Set the additional TGP on top of the base TGP");
|
|
+ATTR_GROUP_INT_VALUE_ONLY_RO(nv_base_tgp, "nv_base_tgp", ASUS_WMI_DEVID_DGPU_BASE_TGP,
|
|
+ "Read the base TGP value");
|
|
+
|
|
+
|
|
ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2",
|
|
"Show the current mode of charging");
|
|
|
|
@@ -753,6 +788,16 @@ static const struct asus_attr_group armoury_attr_groups[] = {
|
|
{ &cores_efficiency_attr_group, ASUS_WMI_DEVID_CORES_MAX },
|
|
{ &cores_performance_attr_group, ASUS_WMI_DEVID_CORES_MAX },
|
|
|
|
+ { &ppt_pl1_spl_attr_group, ASUS_WMI_DEVID_PPT_PL1_SPL },
|
|
+ { &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_PPT_PL2_SPPT },
|
|
+ { &ppt_pl3_fppt_attr_group, ASUS_WMI_DEVID_PPT_FPPT },
|
|
+ { &ppt_apu_sppt_attr_group, ASUS_WMI_DEVID_PPT_APU_SPPT },
|
|
+ { &ppt_platform_sppt_attr_group, ASUS_WMI_DEVID_PPT_PLAT_SPPT },
|
|
+ { &nv_dynamic_boost_attr_group, ASUS_WMI_DEVID_NV_DYN_BOOST },
|
|
+ { &nv_temp_target_attr_group, ASUS_WMI_DEVID_NV_THERM_TARGET },
|
|
+ { &nv_base_tgp_attr_group, ASUS_WMI_DEVID_DGPU_BASE_TGP },
|
|
+ { &nv_tgp_attr_group, ASUS_WMI_DEVID_DGPU_SET_TGP },
|
|
+
|
|
{ &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE },
|
|
{ &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND },
|
|
{ &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE },
|
|
@@ -762,6 +807,9 @@ static const struct asus_attr_group armoury_attr_groups[] = {
|
|
|
|
static int asus_fw_attr_add(void)
|
|
{
|
|
+ const struct power_limits *limits;
|
|
+ bool should_create;
|
|
+ const char *name;
|
|
int err, i;
|
|
|
|
asus_armoury.fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
|
|
@@ -815,17 +863,55 @@ static int asus_fw_attr_add(void)
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(armoury_attr_groups); i++) {
|
|
+ name = armoury_attr_groups[i].attr_group->name;
|
|
+ should_create = true;
|
|
+
|
|
if (!asus_wmi_is_present(armoury_attr_groups[i].wmi_devid))
|
|
continue;
|
|
|
|
- err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
|
|
- armoury_attr_groups[i].attr_group);
|
|
- if (err) {
|
|
- pr_err("Failed to create sysfs-group for %s\n",
|
|
- armoury_attr_groups[i].attr_group->name);
|
|
- goto err_remove_groups;
|
|
+ /*
|
|
+ * Check ROG tunables against initialized limits, don't create attributes for any
|
|
+ * that might have a supported WMI method but no associated data.
|
|
+ */
|
|
+ if (!strcmp(name, "ppt_pl1_spl") || !strcmp(name, "ppt_pl2_sppt") ||
|
|
+ !strcmp(name, "ppt_pl3_fppt") || !strcmp(name, "ppt_apu_sppt") ||
|
|
+ !strcmp(name, "ppt_platform_sppt") || !strcmp(name, "nv_dynamic_boost") ||
|
|
+ !strcmp(name, "nv_temp_target") || !strcmp(name, "nv_base_tgp") ||
|
|
+ !strcmp(name, "dgpu_tgp"))
|
|
+ {
|
|
+ should_create = false;
|
|
+ if (asus_armoury.rog_tunables && asus_armoury.rog_tunables->tuning_limits &&
|
|
+ asus_armoury.rog_tunables->tuning_limits->ac_data) {
|
|
+ /* Must have AC table, and a max value for each attribute */
|
|
+ limits = asus_armoury.rog_tunables->tuning_limits->ac_data;
|
|
+ should_create = limits && (
|
|
+ (!strcmp(name, "ppt_pl1_spl") && limits->ppt_pl1_spl_max) ||
|
|
+ (!strcmp(name, "ppt_pl2_sppt") && limits->ppt_pl2_sppt_max) ||
|
|
+ (!strcmp(name, "ppt_pl3_fppt") && limits->ppt_pl3_fppt_max) ||
|
|
+ (!strcmp(name, "ppt_apu_sppt") && limits->ppt_apu_sppt_max) ||
|
|
+ (!strcmp(name, "ppt_platform_sppt") && limits->ppt_platform_sppt_max) ||
|
|
+ (!strcmp(name, "nv_dynamic_boost") && limits->nv_dynamic_boost_max) ||
|
|
+ (!strcmp(name, "nv_temp_target") && limits->nv_temp_target_max) ||
|
|
+ (!strcmp(name, "nv_base_tgp") && limits->nv_tgp_max) ||
|
|
+ (!strcmp(name, "dgpu_tgp") && limits->nv_tgp_max));
|
|
+
|
|
+ /* Log error so users can report upstream */
|
|
+ if (!should_create)
|
|
+ pr_err("Missing max value on %s for tunable: %s\n",
|
|
+ dmi_get_system_info(DMI_BOARD_NAME), name);
|
|
+ }
|
|
}
|
|
- }
|
|
+
|
|
+ if (should_create) {
|
|
+ err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
|
|
+ armoury_attr_groups[i].attr_group);
|
|
+ if (err) {
|
|
+ pr_err("Failed to create sysfs-group for %s\n",
|
|
+ armoury_attr_groups[i].attr_group->name);
|
|
+ goto err_remove_groups;
|
|
+ }
|
|
+ }
|
|
+}
|
|
|
|
return 0;
|
|
|
|
@@ -849,6 +935,78 @@ static int asus_fw_attr_add(void)
|
|
|
|
/* Init / exit ****************************************************************/
|
|
|
|
+/* Set up the min/max and defaults for ROG tunables */
|
|
+static bool init_rog_tunables(struct rog_tunables *rog)
|
|
+{
|
|
+ const struct dmi_system_id *dmi_id;
|
|
+ const struct power_data *power_data;
|
|
+ const struct power_limits *limits;
|
|
+
|
|
+ /* Match the system against the power_limits table */
|
|
+ dmi_id = dmi_first_match(power_limits);
|
|
+ if (!dmi_id) {
|
|
+ pr_warn("No matching power limits found for this system\n");
|
|
+ // rog->tuning_limits = &default_power_data;
|
|
+ rog->tuning_limits = NULL;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ /* Get the power data for this system */
|
|
+ power_data = dmi_id->driver_data;
|
|
+ if (!power_data) {
|
|
+ pr_info("No power data available for this system\n");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ /* Store the power limits for later use */
|
|
+ rog->tuning_limits = power_data;
|
|
+
|
|
+ if (power_supply_is_system_supplied()) {
|
|
+ limits = power_data->ac_data;
|
|
+ if (!limits) {
|
|
+ pr_warn("No AC power limits available\n");
|
|
+ return false;
|
|
+ }
|
|
+ } else {
|
|
+ limits = power_data->dc_data;
|
|
+ if (!limits && !power_data->ac_data) {
|
|
+ pr_err("No power limits available\n");
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Set initial values */
|
|
+ rog->ppt_pl1_spl = limits->ppt_pl1_spl_def ?
|
|
+ limits->ppt_pl1_spl_def :
|
|
+ limits->ppt_pl1_spl_max;
|
|
+
|
|
+ rog->ppt_pl2_sppt = limits->ppt_pl2_sppt_def ?
|
|
+ limits->ppt_pl2_sppt_def :
|
|
+ limits->ppt_pl2_sppt_max;
|
|
+
|
|
+ rog->ppt_pl3_fppt = limits->ppt_pl3_fppt_def ?
|
|
+ limits->ppt_pl3_fppt_def :
|
|
+ limits->ppt_pl3_fppt_max;
|
|
+
|
|
+ rog->ppt_apu_sppt = limits->ppt_apu_sppt_def ?
|
|
+ limits->ppt_apu_sppt_def :
|
|
+ limits->ppt_apu_sppt_max;
|
|
+
|
|
+ rog->ppt_platform_sppt = limits->ppt_platform_sppt_def ?
|
|
+ limits->ppt_platform_sppt_def :
|
|
+ limits->ppt_platform_sppt_max;
|
|
+
|
|
+ rog->nv_dynamic_boost = limits->nv_dynamic_boost_max;
|
|
+ rog->nv_temp_target = limits->nv_temp_target_max;
|
|
+ rog->nv_tgp = limits->nv_tgp_max;
|
|
+
|
|
+ pr_debug("Power limits initialized for %s (%s power)\n",
|
|
+ dmi_id->matches[0].substr,
|
|
+ power_supply_is_system_supplied() ? "AC" : "DC");
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
static int __init asus_fw_init(void)
|
|
{
|
|
char *wmi_uid;
|
|
@@ -879,6 +1037,16 @@ static int __init asus_fw_init(void)
|
|
}
|
|
}
|
|
|
|
+ asus_armoury.rog_tunables = kzalloc(sizeof(struct rog_tunables), GFP_KERNEL);
|
|
+ if (!asus_armoury.rog_tunables)
|
|
+ return -ENOMEM;
|
|
+ /* Init logs warn/error and the driver should still be usable if init fails */
|
|
+ if (!init_rog_tunables(asus_armoury.rog_tunables)) {
|
|
+ kfree(asus_armoury.rog_tunables);
|
|
+ pr_err("Could not initialise PPT tunable control %d\n", err);
|
|
+ }
|
|
+
|
|
+ /* Must always be last step to ensure data is available */
|
|
err = asus_fw_attr_add();
|
|
if (err)
|
|
return err;
|
|
diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h
|
|
index 5d6bef6d2b12..9adc07ba6d6b 100644
|
|
--- a/drivers/platform/x86/asus-armoury.h
|
|
+++ b/drivers/platform/x86/asus-armoury.h
|
|
@@ -8,6 +8,7 @@
|
|
#ifndef _ASUS_ARMOURY_H_
|
|
#define _ASUS_ARMOURY_H_
|
|
|
|
+#include <linux/dmi.h>
|
|
#include <linux/types.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
@@ -17,18 +18,18 @@ static ssize_t attr_uint_store(struct kobject *kobj, struct kobj_attribute *attr
|
|
const char *buf, size_t count, u32 min, u32 max,
|
|
u32 *store_value, u32 wmi_dev);
|
|
|
|
-static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
- char *buf)
|
|
-{
|
|
- return sysfs_emit(buf, "enumeration\n");
|
|
-}
|
|
-
|
|
static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return sysfs_emit(buf, "integer\n");
|
|
}
|
|
|
|
+static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ return sysfs_emit(buf, "enumeration\n");
|
|
+}
|
|
+
|
|
#define __ASUS_ATTR_RO(_func, _name) \
|
|
{ \
|
|
.attr = { .name = __stringify(_name), .mode = 0444 }, \
|
|
@@ -90,6 +91,20 @@ static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
static struct kobj_attribute attr_##_attrname##_##_prop = \
|
|
__ASUS_ATTR_RO(_attrname, _prop)
|
|
|
|
+/* Requires current_value_show */
|
|
+#define __ATTR_GROUP_INT_VALUE_ONLY(_attrname, _fsname, _dispname) \
|
|
+ __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
|
|
+ static struct kobj_attribute attr_##_attrname##_type = \
|
|
+ __ASUS_ATTR_RO_AS(type, int_type_show); \
|
|
+ static struct attribute *_attrname##_attrs[] = { \
|
|
+ &attr_##_attrname##_current_value.attr, \
|
|
+ &attr_##_attrname##_display_name.attr, \
|
|
+ &attr_##_attrname##_type.attr, NULL \
|
|
+ }; \
|
|
+ static const struct attribute_group _attrname##_attr_group = { \
|
|
+ .name = _fsname, .attrs = _attrname##_attrs \
|
|
+ }
|
|
+
|
|
/* Boolean style enumeration, base macro. Requires adding show/store */
|
|
#define __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname) \
|
|
__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
|
|
@@ -107,6 +122,10 @@ static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
.name = _fsname, .attrs = _attrname##_attrs \
|
|
}
|
|
|
|
+#define ATTR_GROUP_INT_VALUE_ONLY_RO(_attrname, _fsname, _wmi, _dispname) \
|
|
+ __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
|
|
+ __ATTR_GROUP_INT_VALUE_ONLY(_attrname, _fsname, _dispname)
|
|
+
|
|
#define ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \
|
|
__ATTR_CURRENT_INT_RO(_attrname, _wmi); \
|
|
__ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname)
|
|
@@ -178,4 +197,751 @@ static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
.name = _fsname, .attrs = _attrname##_attrs \
|
|
}
|
|
|
|
+/*
|
|
+ * ROG PPT attributes need a little different in setup as they
|
|
+ * require rog_tunables members.
|
|
+ */
|
|
+
|
|
+ #define __ROG_TUNABLE_SHOW(_prop, _attrname, _val) \
|
|
+ static ssize_t _attrname##_##_prop##_show( \
|
|
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf) \
|
|
+ { \
|
|
+ const struct power_limits *limits; \
|
|
+ limits = power_supply_is_system_supplied() ? \
|
|
+ asus_armoury.rog_tunables->tuning_limits->ac_data : \
|
|
+ asus_armoury.rog_tunables->tuning_limits->dc_data; \
|
|
+ if (!limits) \
|
|
+ return -ENODEV; \
|
|
+ return sysfs_emit(buf, "%d\n", limits->_val); \
|
|
+ } \
|
|
+ static struct kobj_attribute attr_##_attrname##_##_prop = \
|
|
+ __ASUS_ATTR_RO(_attrname, _prop)
|
|
+
|
|
+#define __ROG_TUNABLE_SHOW_DEFAULT(_attrname) \
|
|
+ static ssize_t _attrname##_default_value_show( \
|
|
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf) \
|
|
+ { \
|
|
+ const struct power_limits *limits; \
|
|
+ limits = power_supply_is_system_supplied() ? \
|
|
+ asus_armoury.rog_tunables->tuning_limits->ac_data : \
|
|
+ asus_armoury.rog_tunables->tuning_limits->dc_data; \
|
|
+ if (!limits) \
|
|
+ return -ENODEV; \
|
|
+ return sysfs_emit(buf, "%d\n", \
|
|
+ limits->_attrname##_def ? \
|
|
+ limits->_attrname##_def : \
|
|
+ limits->_attrname##_max); \
|
|
+ } \
|
|
+ static struct kobj_attribute attr_##_attrname##_default_value = \
|
|
+ __ASUS_ATTR_RO(_attrname, default_value)
|
|
+
|
|
+#define __ROG_TUNABLE_RW(_attr, _wmi) \
|
|
+ static ssize_t _attr##_current_value_store( \
|
|
+ struct kobject *kobj, struct kobj_attribute *attr, \
|
|
+ const char *buf, size_t count) \
|
|
+ { \
|
|
+ const struct power_limits *limits; \
|
|
+ limits = power_supply_is_system_supplied() ? \
|
|
+ asus_armoury.rog_tunables->tuning_limits->ac_data : \
|
|
+ asus_armoury.rog_tunables->tuning_limits->dc_data; \
|
|
+ if (!limits) \
|
|
+ return -ENODEV; \
|
|
+ return attr_uint_store(kobj, attr, buf, count, \
|
|
+ limits->_attr##_min, \
|
|
+ limits->_attr##_max, \
|
|
+ &asus_armoury.rog_tunables->_attr, \
|
|
+ _wmi); \
|
|
+ } \
|
|
+ static ssize_t _attr##_current_value_show( \
|
|
+ struct kobject *kobj, struct kobj_attribute *attr, \
|
|
+ char *buf) \
|
|
+ { \
|
|
+ return sysfs_emit(buf, "%u\n", \
|
|
+ asus_armoury.rog_tunables->_attr); \
|
|
+ } \
|
|
+ static struct kobj_attribute attr_##_attr##_current_value = \
|
|
+ __ASUS_ATTR_RW(_attr, current_value)
|
|
+
|
|
+#define ATTR_GROUP_ROG_TUNABLE(_attrname, _fsname, _wmi, _dispname) \
|
|
+ __ROG_TUNABLE_RW(_attrname, _wmi); \
|
|
+ __ROG_TUNABLE_SHOW_DEFAULT(_attrname); \
|
|
+ __ROG_TUNABLE_SHOW(min_value, _attrname, _attrname##_min); \
|
|
+ __ROG_TUNABLE_SHOW(max_value, _attrname, _attrname##_max); \
|
|
+ __ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \
|
|
+ __ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
|
|
+ static struct kobj_attribute attr_##_attrname##_type = \
|
|
+ __ASUS_ATTR_RO_AS(type, int_type_show); \
|
|
+ static struct attribute *_attrname##_attrs[] = { \
|
|
+ &attr_##_attrname##_current_value.attr, \
|
|
+ &attr_##_attrname##_default_value.attr, \
|
|
+ &attr_##_attrname##_min_value.attr, \
|
|
+ &attr_##_attrname##_max_value.attr, \
|
|
+ &attr_##_attrname##_scalar_increment.attr, \
|
|
+ &attr_##_attrname##_display_name.attr, \
|
|
+ &attr_##_attrname##_type.attr, \
|
|
+ NULL \
|
|
+ }; \
|
|
+ static const struct attribute_group _attrname##_attr_group = { \
|
|
+ .name = _fsname, .attrs = _attrname##_attrs \
|
|
+ }
|
|
+
|
|
+
|
|
+/* Default is always the maximum value unless *_def is specified */
|
|
+struct power_limits {
|
|
+ u32 ppt_pl1_spl_min;
|
|
+ u32 ppt_pl1_spl_def;
|
|
+ u32 ppt_pl1_spl_max;
|
|
+ u32 ppt_pl2_sppt_min;
|
|
+ u32 ppt_pl2_sppt_def;
|
|
+ u32 ppt_pl2_sppt_max;
|
|
+ u32 ppt_pl3_fppt_min;
|
|
+ u32 ppt_pl3_fppt_def;
|
|
+ u32 ppt_pl3_fppt_max;
|
|
+ u32 ppt_apu_sppt_min;
|
|
+ u32 ppt_apu_sppt_def;
|
|
+ u32 ppt_apu_sppt_max;
|
|
+ u32 ppt_platform_sppt_min;
|
|
+ u32 ppt_platform_sppt_def;
|
|
+ u32 ppt_platform_sppt_max;
|
|
+ /* Nvidia GPU specific, default is always max */
|
|
+ u32 nv_dynamic_boost_def; // unused. exists for macro
|
|
+ u32 nv_dynamic_boost_min;
|
|
+ u32 nv_dynamic_boost_max;
|
|
+ u32 nv_temp_target_def; // unused. exists for macro
|
|
+ u32 nv_temp_target_min;
|
|
+ u32 nv_temp_target_max;
|
|
+ u32 nv_tgp_def; // unused. exists for macro
|
|
+ u32 nv_tgp_min;
|
|
+ u32 nv_tgp_max;
|
|
+};
|
|
+
|
|
+struct power_data {
|
|
+ const struct power_limits *ac_data;
|
|
+ const struct power_limits *dc_data;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * For each avilable attribute there must be a min and a max.
|
|
+ * _def is not required and will be assumed to be default == max if missing.
|
|
+ */
|
|
+static const struct dmi_system_id power_limits[] = {
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "FA507R"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 80
|
|
+ },
|
|
+ .dc_data = NULL
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "FA507X"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 80,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 20,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ .nv_tgp_min = 55,
|
|
+ .nv_tgp_max = 85,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_def = 45,
|
|
+ .ppt_pl1_spl_max = 65,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_def = 54,
|
|
+ .ppt_pl2_sppt_max = 65,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 65,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "FA507Z"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 28,
|
|
+ .ppt_pl1_spl_max = 65,
|
|
+ .ppt_pl2_sppt_min = 28,
|
|
+ .ppt_pl2_sppt_max = 105,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 15,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ .nv_tgp_min = 55,
|
|
+ .nv_tgp_max = 85,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 45,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_max = 60,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "FA607P"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 30,
|
|
+ .ppt_pl1_spl_def = 100,
|
|
+ .ppt_pl1_spl_max = 135,
|
|
+ .ppt_pl2_sppt_min = 30,
|
|
+ .ppt_pl2_sppt_def = 115,
|
|
+ .ppt_pl2_sppt_max = 135,
|
|
+ .ppt_pl3_fppt_min = 30,
|
|
+ .ppt_pl3_fppt_max = 135,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 25,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ .nv_tgp_min = 55,
|
|
+ .nv_tgp_max = 115,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_def = 45,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_def = 60,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ .ppt_pl3_fppt_min = 25,
|
|
+ .ppt_pl3_fppt_max = 80,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "FA617NS"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_apu_sppt_min = 15,
|
|
+ .ppt_apu_sppt_max = 80,
|
|
+ .ppt_platform_sppt_min = 30,
|
|
+ .ppt_platform_sppt_max = 120
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_apu_sppt_min = 25,
|
|
+ .ppt_apu_sppt_max = 35,
|
|
+ .ppt_platform_sppt_min = 45,
|
|
+ .ppt_platform_sppt_max = 100
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "FA617NT"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_apu_sppt_min = 15,
|
|
+ .ppt_apu_sppt_max = 80,
|
|
+ .ppt_platform_sppt_min = 30,
|
|
+ .ppt_platform_sppt_max = 115
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_apu_sppt_min = 15,
|
|
+ .ppt_apu_sppt_max = 45,
|
|
+ .ppt_platform_sppt_min = 30,
|
|
+ .ppt_platform_sppt_max = 50
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "FA617XS"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_apu_sppt_min = 15,
|
|
+ .ppt_apu_sppt_max = 80,
|
|
+ .ppt_platform_sppt_min = 30,
|
|
+ .ppt_platform_sppt_max = 120,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_apu_sppt_min = 25,
|
|
+ .ppt_apu_sppt_max = 35,
|
|
+ .ppt_platform_sppt_min = 45,
|
|
+ .ppt_platform_sppt_max = 100,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "FX507Z"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 28,
|
|
+ .ppt_pl1_spl_max = 90,
|
|
+ .ppt_pl2_sppt_min = 28,
|
|
+ .ppt_pl2_sppt_max = 135,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 15,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 45,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_max = 60,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GA401Q"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 15,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ },
|
|
+ .dc_data = NULL
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ // This model is full AMD. No Nvidia dGPU.
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GA402R"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_apu_sppt_min = 15,
|
|
+ .ppt_apu_sppt_max = 80,
|
|
+ .ppt_platform_sppt_min = 30,
|
|
+ .ppt_platform_sppt_max = 115,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_apu_sppt_min = 25,
|
|
+ .ppt_apu_sppt_def = 30,
|
|
+ .ppt_apu_sppt_max = 45,
|
|
+ .ppt_platform_sppt_min = 40,
|
|
+ .ppt_platform_sppt_max = 60,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GA402X"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_def = 35,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_def = 65,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 80,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 35,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_max = 35,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 65,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GA403U"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 80
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 35,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_max = 35,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 65
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GA503R"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_def = 35,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_def = 65,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 80,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 20,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_def = 25,
|
|
+ .ppt_pl1_spl_max = 65,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_def = 54,
|
|
+ .ppt_pl2_sppt_max = 60,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 65
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GA605W"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 80,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 20,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ .nv_tgp_min = 55,
|
|
+ .nv_tgp_max = 85,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 35,
|
|
+ .ppt_pl2_sppt_min = 31,
|
|
+ .ppt_pl2_sppt_max = 44,
|
|
+ .ppt_pl3_fppt_min = 45,
|
|
+ .ppt_pl3_fppt_max = 65,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GU604V"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 65,
|
|
+ .ppt_pl1_spl_max = 120,
|
|
+ .ppt_pl2_sppt_min = 65,
|
|
+ .ppt_pl2_sppt_max = 150,
|
|
+ /* Only allowed in AC mode */
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 25,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 40,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_def = 40,
|
|
+ .ppt_pl2_sppt_max = 60,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GU605M"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 28,
|
|
+ .ppt_pl1_spl_max = 90,
|
|
+ .ppt_pl2_sppt_min = 28,
|
|
+ .ppt_pl2_sppt_max = 135,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 20,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 35,
|
|
+ .ppt_pl2_sppt_min = 38,
|
|
+ .ppt_pl2_sppt_max = 53,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "GV601V"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 28,
|
|
+ .ppt_pl1_spl_def = 100,
|
|
+ .ppt_pl1_spl_max = 110,
|
|
+ .ppt_pl2_sppt_min = 28,
|
|
+ .ppt_pl2_sppt_max = 135,
|
|
+ /* Only allowed in AC mode */
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 20,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 40,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_def = 40,
|
|
+ .ppt_pl2_sppt_max = 60,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "G513Q"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ /* Yes this laptop is very limited */
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 15,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ },
|
|
+ .dc_data = NULL
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "G614J"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 28,
|
|
+ .ppt_pl1_spl_max = 140,
|
|
+ .ppt_pl2_sppt_min = 28,
|
|
+ .ppt_pl2_sppt_max = 175,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 25,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 55,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_max = 70,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "G634J"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 28,
|
|
+ .ppt_pl1_spl_max = 140,
|
|
+ .ppt_pl2_sppt_min = 28,
|
|
+ .ppt_pl2_sppt_max = 175,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 25,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 55,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_max = 70,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "G814J"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 28,
|
|
+ .ppt_pl1_spl_max = 140,
|
|
+ .ppt_pl2_sppt_min = 28,
|
|
+ .ppt_pl2_sppt_max = 140,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 25,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 55,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_max = 70,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "G834J"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 28,
|
|
+ .ppt_pl1_spl_max = 140,
|
|
+ .ppt_pl2_sppt_min = 28,
|
|
+ .ppt_pl2_sppt_max = 175,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 25,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 55,
|
|
+ .ppt_pl2_sppt_min = 25,
|
|
+ .ppt_pl2_sppt_max = 70,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "H7606W"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 15,
|
|
+ .ppt_pl1_spl_max = 80,
|
|
+ .ppt_pl2_sppt_min = 35,
|
|
+ .ppt_pl2_sppt_max = 80,
|
|
+ .ppt_pl3_fppt_min = 35,
|
|
+ .ppt_pl3_fppt_max = 80,
|
|
+ .nv_dynamic_boost_min = 5,
|
|
+ .nv_dynamic_boost_max = 20,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ .nv_tgp_min = 55,
|
|
+ .nv_tgp_max = 85,
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 25,
|
|
+ .ppt_pl1_spl_max = 35,
|
|
+ .ppt_pl2_sppt_min = 31,
|
|
+ .ppt_pl2_sppt_max = 44,
|
|
+ .ppt_pl3_fppt_min = 45,
|
|
+ .ppt_pl3_fppt_max = 65,
|
|
+ .nv_temp_target_min = 75,
|
|
+ .nv_temp_target_max = 87,
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "RC71"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 7,
|
|
+ .ppt_pl1_spl_max = 30,
|
|
+ .ppt_pl2_sppt_min = 15,
|
|
+ .ppt_pl2_sppt_max = 43,
|
|
+ .ppt_pl3_fppt_min = 15,
|
|
+ .ppt_pl3_fppt_max = 53
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 7,
|
|
+ .ppt_pl1_spl_def = 15,
|
|
+ .ppt_pl1_spl_max = 25,
|
|
+ .ppt_pl2_sppt_min = 15,
|
|
+ .ppt_pl2_sppt_def = 20,
|
|
+ .ppt_pl2_sppt_max = 30,
|
|
+ .ppt_pl3_fppt_min = 15,
|
|
+ .ppt_pl3_fppt_def = 25,
|
|
+ .ppt_pl3_fppt_max = 35
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "RC72"),
|
|
+ },
|
|
+ .driver_data = &(struct power_data){
|
|
+ .ac_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 7,
|
|
+ .ppt_pl1_spl_max = 30,
|
|
+ .ppt_pl2_sppt_min = 15,
|
|
+ .ppt_pl2_sppt_max = 43,
|
|
+ .ppt_pl3_fppt_min = 15,
|
|
+ .ppt_pl3_fppt_max = 53
|
|
+ },
|
|
+ .dc_data = &(struct power_limits){
|
|
+ .ppt_pl1_spl_min = 7,
|
|
+ .ppt_pl1_spl_def = 17,
|
|
+ .ppt_pl1_spl_max = 25,
|
|
+ .ppt_pl2_sppt_min = 15,
|
|
+ .ppt_pl2_sppt_def = 24,
|
|
+ .ppt_pl2_sppt_max = 30,
|
|
+ .ppt_pl3_fppt_min = 15,
|
|
+ .ppt_pl3_fppt_def = 30,
|
|
+ .ppt_pl3_fppt_max = 35
|
|
+ }
|
|
+ },
|
|
+ },
|
|
+ {}
|
|
+};
|
|
+
|
|
#endif /* _ASUS_ARMOURY_H_ */
|
|
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
|
index 3ba0f8fef150..34eddaee5983 100644
|
|
--- a/include/linux/platform_data/x86/asus-wmi.h
|
|
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
|
@@ -285,6 +285,9 @@ struct asus_wmi {
|
|
|
|
#define ASUS_WMI_DEVID_APU_MEM 0x000600C1
|
|
|
|
+#define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
|
|
+#define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
|
|
+
|
|
/* gpu mux switch, 0 = dGPU, 1 = Optimus */
|
|
#define ASUS_WMI_DEVID_GPU_MUX 0x00090016
|
|
#define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From 51da1eea5ae6e86da671d31aa94693af04e1d1e2 Mon Sep 17 00:00:00 2001
|
|
From: "Luke D. Jones" <luke@ljones.dev>
|
|
Date: Tue, 28 Jan 2025 14:44:43 +1300
|
|
Subject: [PATCH 27/28] asus-wmi: change quiet to low-power
|
|
|
|
Signed-off-by: Luke D. Jones <luke@ljones.dev>
|
|
---
|
|
drivers/platform/x86/asus-wmi.c | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
|
index 7e9878851f40..9a71a49d2c6a 100644
|
|
--- a/drivers/platform/x86/asus-wmi.c
|
|
+++ b/drivers/platform/x86/asus-wmi.c
|
|
@@ -3771,7 +3771,7 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
|
|
*profile = PLATFORM_PROFILE_PERFORMANCE;
|
|
break;
|
|
case ASUS_THROTTLE_THERMAL_POLICY_SILENT:
|
|
- *profile = PLATFORM_PROFILE_QUIET;
|
|
+ *profile = PLATFORM_PROFILE_LOW_POWER;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
@@ -3795,7 +3795,7 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
|
|
case PLATFORM_PROFILE_BALANCED:
|
|
tp = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
|
|
break;
|
|
- case PLATFORM_PROFILE_QUIET:
|
|
+ case PLATFORM_PROFILE_LOW_POWER:
|
|
tp = ASUS_THROTTLE_THERMAL_POLICY_SILENT;
|
|
break;
|
|
default:
|
|
--
|
|
2.47.1
|
|
|
|
|
|
From e1cf942d39b9999fc1694a232c9d28dcaf51b5b0 Mon Sep 17 00:00:00 2001
|
|
From: Luke Jones <luke@ljones.dev>
|
|
Date: Wed, 5 Feb 2025 12:37:47 +1300
|
|
Subject: [PATCH 28/28] backport: fix fw_attr use
|
|
|
|
Signed-off-by: Luke Jones <luke@ljones.dev>
|
|
---
|
|
drivers/platform/x86/asus-armoury.c | 14 +++++++++++---
|
|
1 file changed, 11 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
|
|
index 5768997ea2d3..47f3bf381825 100644
|
|
--- a/drivers/platform/x86/asus-armoury.c
|
|
+++ b/drivers/platform/x86/asus-armoury.c
|
|
@@ -82,6 +82,8 @@ struct rog_tunables {
|
|
u32 nv_tgp;
|
|
};
|
|
|
|
+static const struct class *fw_attr_class;
|
|
+
|
|
struct asus_armoury_priv {
|
|
struct device *fw_attr_dev;
|
|
struct kset *fw_attr_kset;
|
|
@@ -812,7 +814,11 @@ static int asus_fw_attr_add(void)
|
|
const char *name;
|
|
int err, i;
|
|
|
|
- asus_armoury.fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
|
|
+ err = fw_attributes_class_get(&fw_attr_class);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ asus_armoury.fw_attr_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
|
|
NULL, "%s", DRIVER_NAME);
|
|
if (IS_ERR(asus_armoury.fw_attr_dev)) {
|
|
err = PTR_ERR(asus_armoury.fw_attr_dev);
|
|
@@ -928,8 +934,9 @@ static int asus_fw_attr_add(void)
|
|
err_destroy_kset:
|
|
kset_unregister(asus_armoury.fw_attr_kset);
|
|
err_destroy_classdev:
|
|
+ device_destroy(fw_attr_class, MKDEV(0, 0));
|
|
fail_class_get:
|
|
- device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
|
+ fw_attributes_class_put();
|
|
return err;
|
|
}
|
|
|
|
@@ -1060,7 +1067,8 @@ static void __exit asus_fw_exit(void)
|
|
|
|
sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
|
|
kset_unregister(asus_armoury.fw_attr_kset);
|
|
- device_destroy(&firmware_attributes_class, MKDEV(0, 0));
|
|
+ device_destroy(fw_attr_class, MKDEV(0, 0));
|
|
+ fw_attributes_class_put();
|
|
|
|
mutex_unlock(&asus_armoury.mutex);
|
|
}
|
|
--
|
|
2.47.1
|
|
|