aboutsummaryrefslogtreecommitdiff
path: root/SOURCES/asus-linux.patch
diff options
context:
space:
mode:
Diffstat (limited to 'SOURCES/asus-linux.patch')
-rw-r--r--SOURCES/asus-linux.patch5069
1 files changed, 0 insertions, 5069 deletions
diff --git a/SOURCES/asus-linux.patch b/SOURCES/asus-linux.patch
deleted file mode 100644
index e22d980..0000000
--- a/SOURCES/asus-linux.patch
+++ /dev/null
@@ -1,5069 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jan200101 <sentrycraft123@gmail.com>
-Date: Sun, 8 Sep 2024 01:49:57 +0200
-Subject: [PATCH] asus-linux 6.10.6
-
-Signed-off-by: Jan200101 <sentrycraft123@gmail.com>
----
- drivers/hid/Kconfig | 9 +
- drivers/hid/Makefile | 1 +
- drivers/hid/hid-asus-ally.c | 2284 +++++++++++++++++++
- drivers/hid/hid-asus-ally.h | 544 +++++
- drivers/hid/hid-asus.c | 29 +
- drivers/hid/hid-ids.h | 3 +
- drivers/platform/x86/Kconfig | 14 +
- drivers/platform/x86/Makefile | 1 +
- drivers/platform/x86/amd/pmf/pmf-quirks.c | 9 +-
- drivers/platform/x86/asus-armoury.c | 1049 +++++++++
- drivers/platform/x86/asus-armoury.h | 259 +++
- drivers/platform/x86/asus-wmi.c | 254 ++-
- drivers/platform/x86/intel/int3472/Makefile | 9 +-
- drivers/platform/x86/intel/int3472/common.c | 7 +
- include/linux/platform_data/x86/asus-wmi.h | 56 +
- sound/pci/hda/patch_realtek.c | 8 +
- 16 files changed, 4466 insertions(+), 70 deletions(-)
- create mode 100644 drivers/hid/hid-asus-ally.c
- create mode 100644 drivers/hid/hid-asus-ally.h
- create mode 100644 drivers/platform/x86/asus-armoury.c
- create mode 100644 drivers/platform/x86/asus-armoury.h
-
-diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
-index 08446c89eff6..ea0dbe9111c4 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 ce71b53ea6c5..98b346d8f783 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..fd3d23b2c284
---- /dev/null
-+++ b/drivers/hid/hid-asus-ally.c
-@@ -0,0 +1,2284 @@
-+// 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/delay.h"
-+#include "linux/device.h"
-+#include "linux/err.h"
-+#include "linux/input-event-codes.h"
-+#include "linux/kstrtox.h"
-+#include "linux/printk.h"
-+#include "linux/slab.h"
-+#include "linux/stddef.h"
-+#include "linux/sysfs.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 ALLY_CFG_INTF_IN_ADDRESS 0x83
-+#define ALLY_CFG_INTF_OUT_ADDRESS 0x04
-+#define ALLY_X_INTERFACE_ADDRESS 0x87
-+
-+#define FEATURE_KBD_LED_REPORT_ID1 0x5d
-+#define FEATURE_KBD_LED_REPORT_ID2 0x5e
-+
-+enum ROG_ALLY_TYPE {
-+ ROG_ALLY_TYPE,
-+ ROG_ALLY_TYPE_X,
-+};
-+
-+static const struct hid_device_id rog_ally_devices[] = {
-+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY),
-+ .driver_data = ROG_ALLY_TYPE },
-+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X),
-+ .driver_data = ROG_ALLY_TYPE_X },
-+ {}
-+};
-+
-+struct KeyCode {
-+ const char *label;
-+ u8 code;
-+};
-+
-+static const struct KeyCode gamepad_codes[] = {
-+ { "PAD_A", 0x01 }, { "PAD_B", 0x02 }, { "PAD_X", 0x03 },
-+ { "PAD_Y", 0x04 }, { "PAD_LB", 0x05 }, { "PAD_RB", 0x06 },
-+ { "PAD_LS", 0x07 }, { "PAD_RS", 0x08 }, { "PAD_DPAD_UP", 0x09 },
-+ { "PAD_DPAD_DOWN", 0x0a }, { "PAD_DPAD_LEFT", 0x0b }, { "PAD_DPAD_RIGHT", 0x0c },
-+ { "PAD_VIEW", 0x11 }, { "PAD_MENU", 0x12 }, { "PAD_XBOX", 0x13 }
-+};
-+
-+static const struct KeyCode keyboard_codes[] = { { "KB_M1", 0x8f },
-+ { "KB_M2", 0x8e },
-+ { "KB_ESC", 0x76 },
-+ { "KB_F1", 0x50 },
-+ { "KB_F2", 0x60 },
-+ { "KB_F3", 0x40 },
-+ { "KB_F4", 0x0c },
-+ { "KB_F5", 0x03 },
-+ { "KB_F6", 0x0b },
-+ { "KB_F7", 0x80 },
-+ { "KB_F8", 0x0a },
-+ { "KB_F9", 0x01 },
-+ { "KB_F10", 0x09 },
-+ { "KB_F11", 0x78 },
-+ { "KB_F12", 0x07 },
-+ { "KB_F14", 0x10 },
-+ { "KB_F15", 0x18 },
-+ { "KB_BACKTICK", 0x0e },
-+ { "KB_1", 0x16 },
-+ { "KB_2", 0x1e },
-+ { "KB_3", 0x26 },
-+ { "KB_4", 0x25 },
-+ { "KB_5", 0x2e },
-+ { "KB_6", 0x36 },
-+ { "KB_7", 0x3d },
-+ { "KB_8", 0x3e },
-+ { "KB_9", 0x46 },
-+ { "KB_0", 0x45 },
-+ { "KB_HYPHEN", 0x4e },
-+ { "KB_EQUALS", 0x55 },
-+ { "KB_BACKSPACE", 0x66 },
-+ { "KB_TAB", 0x0d },
-+ { "KB_Q", 0x15 },
-+ { "KB_W", 0x1d },
-+ { "KB_E", 0x24 },
-+ { "KB_R", 0x2d },
-+ { "KB_T", 0x2d },
-+ { "KB_Y", 0x35 },
-+ { "KB_U", 0x3c },
-+ { "KB_I", 0x43 },
-+ { "KB_O", 0x44 },
-+ { "KB_P", 0x4d },
-+ { "KB_LBRACKET", 0x54 },
-+ { "KB_RBRACKET", 0x5b },
-+ { "KB_BACKSLASH", 0x5d },
-+ { "KB_CAPS", 0x58 },
-+ { "KB_A", 0x1c },
-+ { "KB_S", 0x1b },
-+ { "KB_D", 0x23 },
-+ { "KB_F", 0x2b },
-+ { "KB_G", 0x34 },
-+ { "KB_H", 0x33 },
-+ { "KB_J", 0x3b },
-+ { "KB_K", 0x42 },
-+ { "KB_L", 0x4b },
-+ { "KB_SEMI", 0x4c },
-+ { "KB_QUOTE", 0x52 },
-+ { "KB_RET", 0x5a },
-+ { "KB_LSHIFT", 0x88 },
-+ { "KB_Z", 0x1a },
-+ { "KB_X", 0x22 },
-+ { "KB_C", 0x21 },
-+ { "KB_V", 0x2a },
-+ { "KB_B", 0x32 },
-+ { "KB_N", 0x31 },
-+ { "KB_M", 0x3a },
-+ { "KB_COMMA", 0x41 },
-+ { "KB_PERIOD", 0x49 },
-+ { "KB_FWDSLASH", 0x4a },
-+ { "KB_RSHIFT", 0x89 },
-+ { "KB_LCTL", 0x8c },
-+ { "KB_META", 0x82 },
-+ { "KB_LALT", 0xba },
-+ { "KB_SPACE", 0x29 },
-+ { "KB_RALT", 0x8b },
-+ { "KB_MENU", 0x84 },
-+ { "KB_RCTL", 0x8d },
-+ { "KB_PRNTSCN", 0xc3 },
-+ { "KB_SCRLCK", 0x7e },
-+ { "KB_PAUSE", 0x91 },
-+ { "KB_INS", 0xc2 },
-+ { "KB_HOME", 0x94 },
-+ { "KB_PGUP", 0x96 },
-+ { "KB_DEL", 0xc0 },
-+ { "KB_END", 0x95 },
-+ { "KB_PGDWN", 0x97 },
-+ { "KB_UP_ARROW", 0x99 },
-+ { "KB_DOWN_ARROW", 0x98 },
-+ { "KB_LEFT_ARROW", 0x91 },
-+ { "KB_RIGHT_ARROW", 0x9b },
-+ { "NUMPAD_LOCK", 0x77 },
-+ { "NUMPAD_FWDSLASH", 0x90 },
-+ { "NUMPAD_ASTERISK", 0x7c },
-+ { "NUMPAD_HYPHEN", 0x7b },
-+ { "NUMPAD_0", 0x70 },
-+ { "NUMPAD_1", 0x69 },
-+ { "NUMPAD_2", 0x72 },
-+ { "NUMPAD_3", 0x7a },
-+ { "NUMPAD_4", 0x6b },
-+ { "NUMPAD_5", 0x73 },
-+ { "NUMPAD_6", 0x74 },
-+ { "NUMPAD_7", 0x6c },
-+ { "NUMPAD_8", 0x75 },
-+ { "NUMPAD_9", 0x7d },
-+ { "NUMPAD_PLUS", 0x79 },
-+ { "NUMPAD_ENTER", 0x81 },
-+ { "NUMPAD_PERIOD", 0x71 } };
-+
-+static const struct KeyCode mouse_codes[] = { { "MOUSE_LCLICK", 0x01 },
-+ { "MOUSE_RCLICK", 0x02 },
-+ { "MOUSE_MCLICK", 0x03 },
-+ { "MOUSE_WHEEL_UP", 0x04 },
-+ { "MOUSE_WHEEL_DOWN", 0x05 } };
-+
-+static const struct KeyCode media_codes[] = {
-+ { "MEDIA_SCREENSHOT", 0x16 }, { "MEDIA_SHOW_KEYBOARD", 0x19 },
-+ { "MEDIA_SHOW_DESKTOP", 0x1c }, { "MEDIA_START_RECORDING", 0x1e },
-+ { "MEDIA_MIC_OFF", 0x01 }, { "MEDIA_VOL_DOWN", 0x02 },
-+ { "MEDIA_VOL_UP", 0x03 }
-+};
-+
-+/* 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_leds {
-+ struct hid_device *hdev;
-+ /* Need two dev here to enable the 3 step brightness */
-+ struct led_classdev led_bright_dev;
-+ struct led_classdev_mc led_rgb_dev;
-+ struct work_struct work;
-+ spinlock_t lock;
-+
-+ bool removed;
-+
-+ /* Update the main brightness 0-2 using a single raw write */
-+ bool update_bright;
-+ unsigned int brightness;
-+
-+ /* Update the RGB only to keep write efficient */
-+ bool update_rgb;
-+ uint8_t gamepad_red;
-+ uint8_t gamepad_green;
-+ uint8_t gamepad_blue;
-+};
-+
-+/* 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: [joysticks/triggers][left(2 bytes), right(2 bytes)]
-+ * joysticks: 2 bytes: inner, outer
-+ * triggers: 2 bytes: lower, upper
-+ * min/max: 0-64
-+ */
-+ u8 deadzones[xpad_mode_mouse][2][4];
-+ /*
-+ * index: left, right
-+ * max: 64
-+ */
-+ u8 vibration_intensity[xpad_mode_mouse][2];
-+ /*
-+ * index: [joysticks][2 byte stepping per point]
-+ * - 4 points of 2 bytes each
-+ * - byte 0 of pair = stick move %
-+ * - byte 1 of pair = stick response %
-+ * - min/max: 1-63
-+ */
-+ bool supports_response_curves;
-+ u8 response_curve[xpad_mode_mouse][2][8];
-+ /*
-+ * left = byte 0, right = byte 1
-+ */
-+ bool supports_anti_deadzones;
-+ u8 anti_deadzones[xpad_mode_mouse][2];
-+ /*
-+ * index: [mode][phys pair][b1, b1 secondary, b2, b2 secondary, blocks of 11]
-+ */
-+ u8 key_mapping[xpad_mode_mouse][btn_pair_lt_rt][MAPPING_BLOCK_LEN];
-+ /*
-+ * index: [mode][button index]
-+ */
-+ u8 turbo_btns[xpad_mode_mouse][TURBO_BLOCK_LEN];
-+ /*
-+ * index: [joystick side][Y-stable, Y-min, Y-max, X-stable, X-min, X-max]
-+ */
-+ u32 js_calibrations[2][6];
-+ /*
-+ * index: [trigger side][stable, max]
-+ */
-+ u32 tr_calibrations[2][2];
-+};
-+
-+static struct ally_drvdata {
-+ struct hid_device *hdev;
-+ struct ally_x_device *ally_x;
-+ struct ally_gamepad_cfg *gamepad_cfg;
-+ struct ally_rgb_leds *led_rgb;
-+} drvdata;
-+
-+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 int asus_dev_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size)
-+{
-+ unsigned char *dmabuf;
-+ int ret;
-+
-+ dmabuf = kmemdup(buf, buf_size, GFP_KERNEL);
-+ if (!dmabuf)
-+ return -ENOMEM;
-+
-+ ret = hid_hw_raw_request(hdev, buf[0], dmabuf, buf_size, HID_FEATURE_REPORT,
-+ HID_REQ_SET_REPORT);
-+ kfree(dmabuf);
-+
-+ return ret;
-+}
-+
-+/**************************************************************************************************/
-+/* 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 configuration */
-+/**************************************************************************************************/
-+static int __gamepad_write_all_to_mcu(struct hid_device *hdev,
-+ struct ally_gamepad_cfg *ally_cfg);
-+
-+static int process_key_code(const struct KeyCode *codes, int code_count, const char *buf_copy,
-+ u8 *out, int out_idx)
-+{
-+ for (int i = 0; i < code_count; i++) {
-+ if (strcmp(buf_copy, codes[i].label) == 0) {
-+ out[out_idx] = codes[i].code;
-+ return 0; // Found
-+ }
-+ }
-+ return -EINVAL; // Not found
-+}
-+
-+static int __string_to_key_code(const char *buf, u8 *out, int out_len)
-+{
-+ char buf_copy[32];
-+ u8 *save_buf;
-+
-+ if (out_len != BTN_CODE_LEN)
-+ return -EINVAL;
-+
-+ save_buf = kzalloc(out_len, GFP_KERNEL);
-+ if (!save_buf)
-+ return -ENOMEM;
-+ memcpy(save_buf, out, out_len);
-+ memset(out, 0, out_len); /* always clear before adjusting */
-+
-+ strscpy(buf_copy, buf);
-+ buf_copy[strcspn(buf_copy, "\n")] = 0;
-+
-+ /* Gamepad group */
-+ out[0] = 0x01;
-+ if (process_key_code(gamepad_codes, ARRAY_SIZE(gamepad_codes), buf_copy, out, 1) == 0)
-+ goto success;
-+
-+ /* Keyboard group */
-+ out[0] = 0x02;
-+ if (process_key_code(keyboard_codes, ARRAY_SIZE(keyboard_codes), buf_copy, out, 2) == 0)
-+ goto success;
-+
-+ /* Mouse group */
-+ out[0] = 0x03;
-+ if (process_key_code(mouse_codes, ARRAY_SIZE(mouse_codes), buf_copy, out, 4) == 0)
-+ goto success;
-+
-+ /* Media group */
-+ out[0] = 0x05;
-+ if (process_key_code(media_codes, ARRAY_SIZE(media_codes), buf_copy, out, 3) == 0)
-+ goto success;
-+
-+ /* Restore bytes if invalid input */
-+ memcpy(out, save_buf, out_len);
-+ kfree(save_buf);
-+ return -EINVAL;
-+
-+success:
-+ kfree(save_buf);
-+ return 0;
-+}
-+
-+static const char *key_code_to_string(const struct KeyCode *codes, int code_count, u8 code)
-+{
-+ for (int i = 0; i < code_count; i++) {
-+ if (codes[i].code == code)
-+ return codes[i].label;
-+ }
-+ return "";
-+}
-+
-+static u8 *__get_btn_block(struct ally_gamepad_cfg *ally_cfg, enum btn_pair pair,
-+ enum btn_pair_side side, bool secondary)
-+{
-+ int offs;
-+
-+ offs = side ? MAPPING_BLOCK_LEN / 2 : 0;
-+ offs = secondary ? offs + BTN_CODE_LEN : offs;
-+ return ally_cfg->key_mapping[ally_cfg->mode - 1][pair - 1] + offs;
-+}
-+
-+static const char *__btn_map_to_string(struct ally_gamepad_cfg *ally_cfg, enum btn_pair pair,
-+ enum btn_pair_side side, bool secondary)
-+{
-+ u8 *out_arg = __get_btn_block(ally_cfg, pair, side, secondary);
-+
-+ switch (out_arg[0]) {
-+ case 0x01: // Gamepad buttons
-+ return key_code_to_string(gamepad_codes, ARRAY_SIZE(gamepad_codes), out_arg[1]);
-+ case 0x02: // Keyboard keys
-+ return key_code_to_string(keyboard_codes, ARRAY_SIZE(keyboard_codes),
-+ out_arg[2]);
-+ case 0x03: // Mouse buttons
-+ return key_code_to_string(mouse_codes, ARRAY_SIZE(mouse_codes), out_arg[4]);
-+ case 0x05: // Media controls
-+ return key_code_to_string(media_codes, ARRAY_SIZE(media_codes), out_arg[3]);
-+ default:
-+ return "";
-+ }
-+}
-+
-+/* ASUS ROG Ally device specific attributes */
-+
-+/* This should be called before any attempts to set device functions */
-+static int __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_warn(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_warn(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;
-+}
-+
-+/* BUTTON REMAPPING *******************************************************************************/
-+static void __btn_pair_to_pkt(struct ally_gamepad_cfg *ally_cfg, enum btn_pair pair, u8 *out,
-+ int out_len)
-+{
-+ 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;
-+ memcpy(&out[5], &ally_cfg->key_mapping[ally_cfg->mode - 1][pair - 1],
-+ MAPPING_BLOCK_LEN);
-+}
-+
-+/* Store the button setting in driver data. Does not apply to device until __gamepad_set_mapping */
-+static int __gamepad_mapping_store(struct ally_gamepad_cfg *ally_cfg, const char *buf,
-+ enum btn_pair pair, int side, bool secondary)
-+{
-+ u8 *out_arg;
-+
-+ out_arg = __get_btn_block(ally_cfg, pair, side, secondary);
-+
-+ return __string_to_key_code(buf, out_arg, BTN_CODE_LEN);
-+}
-+
-+/* Apply the mapping pair to the device */
-+static int __gamepad_set_mapping(struct hid_device *hdev, struct ally_gamepad_cfg *ally_cfg,
-+ enum btn_pair pair)
-+{
-+ u8 *hidbuf;
-+ int ret;
-+
-+ ret = __gamepad_check_ready(hdev);
-+ if (ret < 0)
-+ return ret;
-+
-+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
-+ if (!hidbuf)
-+ return -ENOMEM;
-+
-+ __btn_pair_to_pkt(ally_cfg, pair, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
-+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
-+
-+ kfree(hidbuf);
-+
-+ return ret;
-+}
-+
-+static ssize_t btn_mapping_apply_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_write_all_to_mcu(hdev, ally_cfg);
-+ if (ret < 0)
-+ return ret;
-+
-+ return count;
-+}
-+ALLY_DEVICE_ATTR_WO(btn_mapping_apply, apply_all);
-+
-+/* BUTTON TURBO ***********************************************************************************/
-+static int __btn_turbo_index(enum btn_pair pair, int side)
-+{
-+ return (pair - 1) * (2 * TURBO_BLOCK_STEP) + (side * TURBO_BLOCK_STEP);
-+};
-+
-+static int __gamepad_turbo_show(struct device *dev, enum btn_pair pair, int side)
-+{
-+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
-+
-+ if (!drvdata.gamepad_cfg)
-+ return -ENODEV;
-+
-+ return ally_cfg->turbo_btns[ally_cfg->mode - 1][__btn_turbo_index(pair, side)];
-+};
-+
-+static int __gamepad_turbo_store(struct device *dev, const char *buf, enum btn_pair pair,
-+ int side)
-+{
-+ 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 > 16)
-+ return -EINVAL;
-+
-+ ally_cfg->turbo_btns[ally_cfg->mode - 1][__btn_turbo_index(pair, side)] = val;
-+
-+ return 0;
-+};
-+
-+/* button map attributes, regular and macro*/
-+ALLY_BTN_MAPPING(m2, btn_pair_m1_m2, btn_pair_side_left);
-+ALLY_BTN_MAPPING(m1, btn_pair_m1_m2, btn_pair_side_right);
-+ALLY_BTN_MAPPING(a, btn_pair_a_b, btn_pair_side_left);
-+ALLY_BTN_MAPPING(b, btn_pair_a_b, btn_pair_side_right);
-+ALLY_BTN_MAPPING(x, btn_pair_x_y, btn_pair_side_left);
-+ALLY_BTN_MAPPING(y, btn_pair_x_y, btn_pair_side_right);
-+ALLY_BTN_MAPPING(lb, btn_pair_lb_rb, btn_pair_side_left);
-+ALLY_BTN_MAPPING(rb, btn_pair_lb_rb, btn_pair_side_right);
-+ALLY_BTN_MAPPING(ls, btn_pair_ls_rs, btn_pair_side_left);
-+ALLY_BTN_MAPPING(rs, btn_pair_ls_rs, btn_pair_side_right);
-+ALLY_BTN_MAPPING(lt, btn_pair_lt_rt, btn_pair_side_left);
-+ALLY_BTN_MAPPING(rt, btn_pair_lt_rt, btn_pair_side_right);
-+ALLY_BTN_MAPPING(dpad_u, btn_pair_dpad_u_d, btn_pair_side_left);
-+ALLY_BTN_MAPPING(dpad_d, btn_pair_dpad_u_d, btn_pair_side_right);
-+ALLY_BTN_MAPPING(dpad_l, btn_pair_dpad_l_r, btn_pair_side_left);
-+ALLY_BTN_MAPPING(dpad_r, btn_pair_dpad_l_r, btn_pair_side_right);
-+ALLY_BTN_MAPPING(view, btn_pair_view_menu, btn_pair_side_left);
-+ALLY_BTN_MAPPING(menu, btn_pair_view_menu, btn_pair_side_right);
-+
-+static void __gamepad_mapping_xpad_default(struct ally_gamepad_cfg *ally_cfg)
-+{
-+ memcpy(&ally_cfg->key_mapping[0][0], &XPAD_DEF1, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[0][1], &XPAD_DEF2, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[0][2], &XPAD_DEF3, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[0][3], &XPAD_DEF4, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[0][4], &XPAD_DEF5, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[0][5], &XPAD_DEF6, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[0][6], &XPAD_DEF7, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[0][7], &XPAD_DEF8, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[0][8], &XPAD_DEF9, MAPPING_BLOCK_LEN);
-+}
-+
-+static void __gamepad_mapping_wasd_default(struct ally_gamepad_cfg *ally_cfg)
-+{
-+ memcpy(&ally_cfg->key_mapping[1][0], &WASD_DEF1, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[1][1], &WASD_DEF2, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[1][2], &WASD_DEF3, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[1][3], &WASD_DEF4, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[1][4], &WASD_DEF5, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[1][5], &WASD_DEF6, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[1][6], &WASD_DEF7, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[1][7], &WASD_DEF8, MAPPING_BLOCK_LEN);
-+ memcpy(&ally_cfg->key_mapping[1][8], &WASD_DEF9, MAPPING_BLOCK_LEN);
-+}
-+
-+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_mapping_xpad_default(ally_cfg);
-+ break;
-+ case xpad_mode_wasd:
-+ __gamepad_mapping_wasd_default(ally_cfg);
-+ break;
-+ default:
-+ __gamepad_mapping_xpad_default(ally_cfg);
-+ break;
-+ }
-+
-+ return count;
-+}
-+
-+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;
-+
-+ ret = __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_mode;
-+ hidbuf[3] = xpad_cmd_len_mode;
-+ hidbuf[4] = val;
-+
-+ ret = __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_write_all_to_mcu(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);
-+
-+/* 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_write_vibe_intensity_to_mcu(struct hid_device *hdev,
-+ struct ally_gamepad_cfg *ally_cfg)
-+{
-+ u8 *hidbuf;
-+ int ret;
-+
-+ ret = __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_vibe_intensity;
-+ hidbuf[3] = xpad_cmd_len_vibe_intensity;
-+ hidbuf[4] = ally_cfg->vibration_intensity[ally_cfg->mode - 1][btn_pair_side_left];
-+ hidbuf[5] = ally_cfg->vibration_intensity[ally_cfg->mode - 1][btn_pair_side_right];
-+
-+ ret = __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[ally_cfg->mode - 1][btn_pair_side_left],
-+ ally_cfg->vibration_intensity[ally_cfg->mode - 1][btn_pair_side_right]);
-+}
-+
-+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[ally_cfg->mode - 1][btn_pair_side_left] = left;
-+ ally_cfg->vibration_intensity[ally_cfg->mode - 1][btn_pair_side_right] = right;
-+
-+ ret = __gamepad_write_vibe_intensity_to_mcu(hdev, ally_cfg);
-+ if (ret < 0)
-+ return ret;
-+
-+ return count;
-+}
-+
-+ALLY_DEVICE_ATTR_RW(gamepad_vibration_intensity, vibration_intensity);
-+
-+/* ROOT LEVEL ATTRS *******************************************************************************/
-+static struct attribute *gamepad_device_attrs[] = {
-+ &dev_attr_gamepad_mode.attr,
-+ &dev_attr_btn_mapping_reset.attr,
-+ &dev_attr_btn_mapping_apply.attr,
-+ &dev_attr_gamepad_vibration_intensity.attr,
-+ &dev_attr_gamepad_vibration_intensity_index.attr,
-+ NULL
-+};
-+
-+static const struct attribute_group ally_controller_attr_group = {
-+ .attrs = gamepad_device_attrs,
-+};
-+
-+/* ANALOGUE DEADZONES *****************************************************************************/
-+static ssize_t __gamepad_set_deadzones(struct hid_device *hdev,
-+ struct ally_gamepad_cfg *ally_cfg)
-+{
-+ u8 *hidbuf;
-+ int ret;
-+
-+ ret = __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->deadzones[ally_cfg->mode - 1][0][0];
-+ hidbuf[5] = ally_cfg->deadzones[ally_cfg->mode - 1][0][1];
-+ hidbuf[6] = ally_cfg->deadzones[ally_cfg->mode - 1][0][2];
-+ hidbuf[7] = ally_cfg->deadzones[ally_cfg->mode - 1][0][3];
-+
-+ 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->deadzones[ally_cfg->mode - 1][1][0];
-+ hidbuf[5] = ally_cfg->deadzones[ally_cfg->mode - 1][1][1];
-+ hidbuf[6] = ally_cfg->deadzones[ally_cfg->mode - 1][1][2];
-+ hidbuf[7] = ally_cfg->deadzones[ally_cfg->mode - 1][1][3];
-+
-+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
-+ if (ret < 0)
-+ goto end;
-+
-+end:
-+ kfree(hidbuf);
-+ return ret;
-+}
-+
-+static ssize_t __gamepad_store_deadzones(struct ally_gamepad_cfg *ally_cfg, enum xpad_axis axis,
-+ const char *buf)
-+{
-+ int cmd, side, is_tr;
-+ u32 inner, outer;
-+
-+ if (sscanf(buf, "%d %d", &inner, &outer) != 2)
-+ return -EINVAL;
-+
-+ if (inner > 64 || outer > 64 || inner > outer)
-+ return -EINVAL;
-+
-+ is_tr = axis > xpad_axis_xy_right;
-+ side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 2 : 0;
-+ cmd = is_tr ? xpad_cmd_set_js_dz : xpad_cmd_set_tr_dz;
-+
-+ ally_cfg->deadzones[ally_cfg->mode - 1][is_tr][side] = inner;
-+ ally_cfg->deadzones[ally_cfg->mode - 1][is_tr][side + 1] = outer;
-+
-+ return 0;
-+}
-+
-+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_AXIS_DEADZONE(xpad_axis_xy_left, deadzone);
-+ALLY_AXIS_DEADZONE(xpad_axis_xy_right, deadzone);
-+ALLY_AXIS_DEADZONE(xpad_axis_z_left, deadzone);
-+ALLY_AXIS_DEADZONE(xpad_axis_z_right, deadzone);
-+
-+/* ANTI-DEADZONES *********************************************************************************/
-+static ssize_t __gamepad_write_js_ADZ_to_mcu(struct hid_device *hdev,
-+ struct ally_gamepad_cfg *ally_cfg)
-+{
-+ u8 *hidbuf;
-+ int ret;
-+
-+ ret = __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_adz;
-+ hidbuf[3] = xpad_cmd_len_adz;
-+ hidbuf[4] = ally_cfg->anti_deadzones[ally_cfg->mode - 1][btn_pair_side_left];
-+ hidbuf[5] = ally_cfg->anti_deadzones[ally_cfg->mode - 1][btn_pair_side_right];
-+
-+ ret = __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_js_ADZ_store(struct device *dev, const char *buf,
-+ enum btn_pair_side side)
-+{
-+ 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 > 32)
-+ return -EINVAL;
-+
-+ ally_cfg->anti_deadzones[ally_cfg->mode - 1][side] = val;
-+
-+ return ret;
-+}
-+
-+static ssize_t xpad_axis_xy_left_ADZ_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->anti_deadzones[ally_cfg->mode - 1][btn_pair_side_left]);
-+}
-+
-+static ssize_t xpad_axis_xy_left_ADZ_store(struct device *dev, struct device_attribute *attr,
-+ const char *buf, size_t count)
-+{
-+ int ret = __gamepad_js_ADZ_store(dev, buf, btn_pair_side_left);
-+
-+ if (ret)
-+ return ret;
-+
-+ return count;
-+}
-+
-+ALLY_DEVICE_ATTR_RW(xpad_axis_xy_left_ADZ, anti_deadzone);
-+
-+static ssize_t xpad_axis_xy_right_ADZ_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->anti_deadzones[ally_cfg->mode - 1][btn_pair_side_right]);
-+}
-+
-+static ssize_t xpad_axis_xy_right_ADZ_store(struct device *dev, struct device_attribute *attr,
-+ const char *buf, size_t count)
-+{
-+ int ret = __gamepad_js_ADZ_store(dev, buf, btn_pair_side_right);
-+
-+ if (ret)
-+ return ret;
-+
-+ return count;
-+}
-+
-+ALLY_DEVICE_ATTR_RW(xpad_axis_xy_right_ADZ, anti_deadzone);
-+
-+/* JS RESPONSE CURVES *****************************************************************************/
-+static ssize_t rc_point_index_show(struct device *dev, struct device_attribute *attr, char *buf)
-+{
-+ return sysfs_emit(buf, "move response\n");
-+}
-+
-+ALLY_DEVICE_ATTR_RO(rc_point_index, rc_point_index);
-+
-+static ssize_t __gamepad_write_response_curves_to_mcu(struct hid_device *hdev,
-+ struct ally_gamepad_cfg *ally_cfg)
-+{
-+ u8 *hidbuf;
-+ int ret;
-+
-+ ret = __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_response_curve;
-+ hidbuf[3] = xpad_cmd_len_response_curve;
-+ hidbuf[4] = 0x01;
-+ memcpy(&hidbuf[5], &ally_cfg->response_curve[ally_cfg->mode - 1][btn_pair_side_left],
-+ 8);
-+
-+ ret = __gamepad_check_ready(hdev);
-+ if (ret < 0)
-+ goto report_fail;
-+
-+ hidbuf[4] = 0x02;
-+ memcpy(&hidbuf[5], &ally_cfg->response_curve[ally_cfg->mode - 1][btn_pair_side_right],
-+ 8);
-+
-+ ret = __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_store_response_curve(struct device *dev, const char *buf,
-+ enum btn_pair_side side, int point)
-+{
-+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
-+ u32 move, response;
-+ int idx;
-+
-+ idx = (point - 1) * 2;
-+
-+ if (!drvdata.gamepad_cfg)
-+ return -ENODEV;
-+
-+ if (sscanf(buf, "%d %d", &move, &response) != 2)
-+ return -EINVAL;
-+
-+ if (move > 64 || response > 64)
-+ return -EINVAL;
-+
-+ ally_cfg->response_curve[ally_cfg->mode - 1][side][idx] = move;
-+ ally_cfg->response_curve[ally_cfg->mode - 1][side][idx + 1] = response;
-+
-+ return 0;
-+}
-+
-+ALLY_JS_RC_POINT(left, 1, rc_point_);
-+ALLY_JS_RC_POINT(left, 2, rc_point_);
-+ALLY_JS_RC_POINT(left, 3, rc_point_);
-+ALLY_JS_RC_POINT(left, 4, rc_point_);
-+
-+ALLY_JS_RC_POINT(right, 1, rc_point_);
-+ALLY_JS_RC_POINT(right, 2, rc_point_);
-+ALLY_JS_RC_POINT(right, 3, rc_point_);
-+ALLY_JS_RC_POINT(right, 4, rc_point_);
-+
-+/* 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 */
-+ /* [left][index] is Y: stable, min, max. X: stable, min, max */
-+ ally_cfg->js_calibrations[0][3] = (hidbuf[6] << 8) | hidbuf[7];
-+ ally_cfg->js_calibrations[0][4] = (hidbuf[8] << 8) | hidbuf[9];
-+ ally_cfg->js_calibrations[0][5] = (hidbuf[10] << 8) | hidbuf[11];
-+ ally_cfg->js_calibrations[0][0] = (hidbuf[12] << 8) | hidbuf[13];
-+ ally_cfg->js_calibrations[0][1] = (hidbuf[14] << 8) | hidbuf[15];
-+ ally_cfg->js_calibrations[0][2] = (hidbuf[16] << 8) | hidbuf[17];
-+ /* [right][index] is Y: stable, min, max. X: stable, min, max */
-+ ally_cfg->js_calibrations[1][0] = (hidbuf[24] << 8) | hidbuf[25];
-+ ally_cfg->js_calibrations[1][1] = (hidbuf[26] << 8) | hidbuf[27];
-+ ally_cfg->js_calibrations[1][2] = (hidbuf[28] << 8) | hidbuf[29];
-+ ally_cfg->js_calibrations[1][3] = (hidbuf[18] << 8) | hidbuf[19];
-+ ally_cfg->js_calibrations[1][4] = (hidbuf[20] << 8) | hidbuf[21];
-+ ally_cfg->js_calibrations[1][5] = (hidbuf[22] << 8) | hidbuf[23];
-+ } else {
-+ /* Trigger calibration */
-+ /* [left/right][stable/max] */
-+ ally_cfg->tr_calibrations[0][0] = (hidbuf[6] << 8) | hidbuf[7];
-+ ally_cfg->tr_calibrations[0][1] = (hidbuf[8] << 8) | hidbuf[9];
-+ ally_cfg->tr_calibrations[1][0] = (hidbuf[10] << 8) | hidbuf[11];
-+ ally_cfg->tr_calibrations[1][1] = (hidbuf[12] << 8) | hidbuf[13];
-+ }
-+ }
-+
-+cleanup:
-+ kfree(hidbuf);
-+ return ret;
-+}
-+
-+static ssize_t __gamepad_write_cal_to_mcu(struct device *dev, enum xpad_axis axis)
-+{
-+ struct hid_device *hdev = to_hid_device(dev);
-+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
-+ u8 *c, side, pkt_len, data_len;
-+ int ret, cal, checksum = 0;
-+ u8 *hidbuf;
-+ int *head;
-+
-+ if (!drvdata.gamepad_cfg)
-+ return -ENODEV;
-+
-+ ret = __gamepad_check_ready(hdev);
-+ if (ret < 0)
-+ return ret;
-+
-+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
-+ if (!hidbuf)
-+ return -ENOMEM;
-+
-+ side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 1 : 0;
-+ pkt_len = axis > xpad_axis_xy_right ? 0x06 : 0x0E;
-+ data_len = axis > xpad_axis_xy_right ? 2 : 6;
-+ head = axis > xpad_axis_xy_right ? ally_cfg->tr_calibrations[side] :
-+ ally_cfg->js_calibrations[side];
-+
-+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
-+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
-+ hidbuf[2] = xpad_cmd_set_calibration;
-+ hidbuf[3] = pkt_len;
-+ hidbuf[4] = 0x01; /* second command (write calibration) */
-+ hidbuf[5] = axis;
-+ c = &hidbuf[6]; /* pointer to data start */
-+
-+ for (size_t i = 0; i < data_len; i++) {
-+ cal = head[i];
-+ *c = (u8)((cal & 0xff00) >> 8);
-+ checksum += *c;
-+ c += 1;
-+ *c = (u8)(cal & 0xff);
-+ checksum += *c;
-+ c += 1;
-+ }
-+
-+ hidbuf[6 + data_len * 2] = checksum;
-+
-+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
-+ if (ret < 0)
-+ goto report_fail;
-+
-+ memset(hidbuf, 0, FEATURE_ROG_ALLY_REPORT_SIZE);
-+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
-+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
-+ hidbuf[2] = xpad_cmd_set_calibration;
-+ hidbuf[3] = xpad_cmd_len_calibration3;
-+ hidbuf[4] = 0x03; /* second command (apply the calibration that was written) */
-+
-+ 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_cal_store(struct device *dev, const char *buf, enum xpad_axis axis)
-+{
-+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
-+ u32 x_stable, x_min, x_max, y_stable, y_min, y_max, side;
-+
-+ if (!drvdata.gamepad_cfg)
-+ return -ENODEV;
-+
-+ if (axis == xpad_axis_xy_left || axis == xpad_axis_xy_right) {
-+ if (sscanf(buf, "%d %d %d %d %d %d", &x_stable, &x_min, &x_max, &y_stable,
-+ &y_min, &y_max) != 6)
-+ return -EINVAL;
-+
-+ side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 1 : 0;
-+ /* stored in reverse order for easy copy to packet */
-+ ally_cfg->js_calibrations[side][0] = y_stable;
-+ ally_cfg->js_calibrations[side][1] = y_min;
-+ ally_cfg->js_calibrations[side][2] = y_max;
-+ ally_cfg->js_calibrations[side][3] = x_stable;
-+ ally_cfg->js_calibrations[side][4] = x_min;
-+ ally_cfg->js_calibrations[side][5] = x_max;
-+
-+ return __gamepad_write_cal_to_mcu(dev, axis);
-+ }
-+ if (sscanf(buf, "%d %d", &x_stable, &x_max) != 2)
-+ return -EINVAL;
-+
-+ side = axis == xpad_axis_xy_right || axis == xpad_axis_z_right ? 1 : 0;
-+ /* stored in reverse order for easy copy to packet */
-+ ally_cfg->tr_calibrations[side][0] = x_stable;
-+ ally_cfg->tr_calibrations[side][1] = x_max;
-+
-+ return __gamepad_write_cal_to_mcu(dev, axis);
-+}
-+
-+static ssize_t __gamepad_cal_show(struct device *dev, char *buf, enum xpad_axis axis)
-+{
-+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg;
-+ int side = (axis == xpad_axis_xy_right || axis == xpad_axis_z_right) ? 1 : 0;
-+
-+ if (!drvdata.gamepad_cfg)
-+ return -ENODEV;
-+
-+ if (axis == xpad_axis_xy_left || axis == xpad_axis_xy_right) {
-+ return sysfs_emit(
-+ buf, "%d %d %d %d %d %d\n", ally_cfg->js_calibrations[side][3],
-+ ally_cfg->js_calibrations[side][4], ally_cfg->js_calibrations[side][5],
-+ ally_cfg->js_calibrations[side][0], ally_cfg->js_calibrations[side][1],
-+ ally_cfg->js_calibrations[side][2]);
-+ }
-+
-+ return sysfs_emit(buf, "%d %d\n", ally_cfg->tr_calibrations[side][0],
-+ ally_cfg->tr_calibrations[side][1]);
-+}
-+
-+ALLY_CAL_ATTR(xpad_axis_xy_left_cal, xpad_axis_xy_left, calibration);
-+ALLY_CAL_ATTR(xpad_axis_xy_right_cal, xpad_axis_xy_right, calibration);
-+ALLY_CAL_ATTR(xpad_axis_z_left_cal, xpad_axis_z_left, calibration);
-+ALLY_CAL_ATTR(xpad_axis_z_right_cal, xpad_axis_z_right, calibration);
-+
-+static ssize_t xpad_axis_xy_cal_index_show(struct device *dev, struct device_attribute *attr,
-+ char *buf)
-+{
-+ return sysfs_emit(buf, "x_stable x_min x_max y_stable y_min y_max\n");
-+}
-+
-+ALLY_DEVICE_ATTR_RO(xpad_axis_xy_cal_index, calibration_index);
-+
-+static ssize_t xpad_axis_z_cal_index_show(struct device *dev, struct device_attribute *attr,
-+ char *buf)
-+{
-+ return sysfs_emit(buf, "z_stable z_max\n");
-+}
-+
-+ALLY_DEVICE_ATTR_RO(xpad_axis_z_cal_index, calibration_index);
-+
-+static ssize_t __gamepad_cal_reset(struct device *dev, const char *buf, enum xpad_axis axis)
-+{
-+ struct hid_device *hdev = to_hid_device(dev);
-+ u8 *hidbuf;
-+ int ret;
-+
-+ ret = __gamepad_check_ready(hdev);
-+ if (ret < 0)
-+ return ret;
-+
-+ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
-+ if (!hidbuf)
-+ return -ENOMEM;
-+
-+ /* Write the reset value, then apply it */
-+ for (u8 cmd = 0x02; cmd <= 0x03; cmd++) {
-+ memset(hidbuf, 0, FEATURE_ROG_ALLY_REPORT_SIZE);
-+ hidbuf[0] = FEATURE_ROG_ALLY_REPORT_ID;
-+ hidbuf[1] = FEATURE_ROG_ALLY_CODE_PAGE;
-+ hidbuf[2] = xpad_cmd_set_calibration;
-+ hidbuf[3] = (cmd == 0x02) ? xpad_cmd_len_calibration2 :
-+ xpad_cmd_len_calibration3;
-+ hidbuf[4] = cmd;
-+ hidbuf[5] = axis;
-+
-+ ret = asus_dev_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
-+ if (ret < 0)
-+ break;
-+ }
-+
-+ __gamepad_get_calibration(hdev);
-+
-+ kfree(hidbuf);
-+ return ret;
-+}
-+
-+ALLY_CAL_RESET_ATTR(xpad_axis_xy_left_cal_reset, xpad_axis_xy_left, calibration_reset);
-+ALLY_CAL_RESET_ATTR(xpad_axis_xy_right_cal_reset, xpad_axis_xy_right, calibration_reset);
-+ALLY_CAL_RESET_ATTR(xpad_axis_z_left_cal_reset, xpad_axis_z_left, calibration_reset);
-+ALLY_CAL_RESET_ATTR(xpad_axis_z_right_cal_reset, xpad_axis_z_right, calibration_reset);
-+
-+static struct attribute *gamepad_axis_xy_left_attrs[] = {
-+ &dev_attr_xpad_axis_xy_left_deadzone.attr,
-+ &dev_attr_axis_xyz_deadzone_index.attr,
-+ &dev_attr_xpad_axis_xy_left_ADZ.attr,
-+ &dev_attr_xpad_axis_xy_left_cal_reset.attr,
-+ &dev_attr_xpad_axis_xy_left_cal.attr,
-+ &dev_attr_xpad_axis_xy_cal_index.attr,
-+ &dev_attr_rc_point_left_1.attr,
-+ &dev_attr_rc_point_left_2.attr,
-+ &dev_attr_rc_point_left_3.attr,
-+ &dev_attr_rc_point_left_4.attr,
-+ &dev_attr_rc_point_index.attr,
-+ NULL
-+};
-+static const struct attribute_group ally_controller_axis_xy_left_attr_group = {
-+ .name = "axis_xy_left",
-+ .attrs = gamepad_axis_xy_left_attrs,
-+};
-+
-+static struct attribute *gamepad_axis_xy_right_attrs[] = {
-+ &dev_attr_xpad_axis_xy_right_deadzone.attr,
-+ &dev_attr_axis_xyz_deadzone_index.attr,
-+ &dev_attr_xpad_axis_xy_right_ADZ.attr,
-+ &dev_attr_xpad_axis_xy_right_cal_reset.attr,
-+ &dev_attr_xpad_axis_xy_right_cal.attr,
-+ &dev_attr_xpad_axis_xy_cal_index.attr,
-+ &dev_attr_rc_point_right_1.attr,
-+ &dev_attr_rc_point_right_2.attr,
-+ &dev_attr_rc_point_right_3.attr,
-+ &dev_attr_rc_point_right_4.attr,
-+ &dev_attr_rc_point_index.attr,
-+ NULL
-+};
-+static const struct attribute_group ally_controller_axis_xy_right_attr_group = {
-+ .name = "axis_xy_right",
-+ .attrs = gamepad_axis_xy_right_attrs,
-+};
-+
-+static struct attribute *gamepad_axis_z_left_attrs[] = {
-+ &dev_attr_xpad_axis_z_left_deadzone.attr, &dev_attr_axis_xyz_deadzone_index.attr,
-+ &dev_attr_xpad_axis_z_left_cal.attr, &dev_attr_xpad_axis_z_cal_index.attr,
-+ &dev_attr_xpad_axis_z_left_cal_reset.attr, NULL
-+};
-+static const struct attribute_group ally_controller_axis_z_left_attr_group = {
-+ .name = "axis_z_left",
-+ .attrs = gamepad_axis_z_left_attrs,
-+};
-+
-+static struct attribute *gamepad_axis_z_right_attrs[] = {
-+ &dev_attr_xpad_axis_z_right_deadzone.attr, &dev_attr_axis_xyz_deadzone_index.attr,
-+ &dev_attr_xpad_axis_z_right_cal.attr, &dev_attr_xpad_axis_z_cal_index.attr,
-+ &dev_attr_xpad_axis_z_right_cal_reset.attr, NULL
-+};
-+static const struct attribute_group ally_controller_axis_z_right_attr_group = {
-+ .name = "axis_z_right",
-+ .attrs = gamepad_axis_z_right_attrs,
-+};
-+
-+static const struct attribute_group *gamepad_device_attr_groups[] = {
-+ &ally_controller_attr_group,
-+ &ally_controller_axis_xy_left_attr_group,
-+ &ally_controller_axis_xy_right_attr_group,
-+ &ally_controller_axis_z_left_attr_group,
-+ &ally_controller_axis_z_right_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_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 int __gamepad_write_all_to_mcu(struct hid_device *hdev,
-+ struct ally_gamepad_cfg *ally_cfg)
-+{
-+ u8 *hidbuf;
-+ int ret;
-+
-+ ret = __gamepad_set_mapping(hdev, ally_cfg, btn_pair_dpad_u_d);
-+ if (ret < 0)
-+ return ret;
-+ ret = __gamepad_set_mapping(hdev, ally_cfg, btn_pair_dpad_l_r);
-+ if (ret < 0)
-+ return ret;
-+ ret = __gamepad_set_mapping(hdev, ally_cfg, btn_pair_ls_rs);
-+ if (ret < 0)
-+ return ret;
-+ ret = __gamepad_set_mapping(hdev, ally_cfg, btn_pair_lb_rb);
-+ if (ret < 0)
-+ return ret;
-+ ret = __gamepad_set_mapping(hdev, ally_cfg, btn_pair_a_b);
-+ if (ret < 0)
-+ return ret;
-+ ret = __gamepad_set_mapping(hdev, ally_cfg, btn_pair_x_y);
-+ if (ret < 0)
-+ return ret;
-+ ret = __gamepad_set_mapping(hdev, ally_cfg, btn_pair_view_menu);
-+ if (ret < 0)
-+ return ret;
-+ ret = __gamepad_set_mapping(hdev, ally_cfg, btn_pair_m1_m2);
-+ if (ret < 0)
-+ return ret;
-+ __gamepad_set_mapping(hdev, ally_cfg, btn_pair_lt_rt);
-+ if (ret < 0)
-+ return ret;
-+ __gamepad_set_deadzones(hdev, ally_cfg);
-+ if (ret < 0)
-+ return ret;
-+ __gamepad_write_js_ADZ_to_mcu(hdev, ally_cfg);
-+ if (ret < 0)
-+ return ret;
-+ __gamepad_write_vibe_intensity_to_mcu(hdev, ally_cfg);
-+ if (ret < 0)
-+ return ret;
-+ __gamepad_write_response_curves_to_mcu(hdev, ally_cfg);
-+ if (ret < 0)
-+ return ret;
-+ ret = __gamepad_check_ready(hdev);
-+ if (ret < 0)
-+ return 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;
-+ memcpy(&hidbuf[4], ally_cfg->turbo_btns[ally_cfg->mode - 1], TURBO_BLOCK_LEN);
-+ 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 i, err;
-+
-+ ally_cfg = devm_kzalloc(&hdev->dev, sizeof(*ally_cfg), GFP_KERNEL);
-+ if (!ally_cfg)
-+ return ERR_PTR(-ENOMEM);
-+ ally_cfg->hdev = hdev;
-+
-+ input_dev = devm_input_allocate_device(&hdev->dev);
-+ if (!input_dev) {
-+ err = -ENOMEM;
-+ goto free_ally_cfg;
-+ }
-+ ally_cfg->input = input_dev;
-+
-+ 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->mode = xpad_mode_game;
-+ for (i = 0; i < xpad_mode_mouse; i++) {
-+ ally_cfg->deadzones[i][0][1] = 64;
-+ ally_cfg->deadzones[i][0][3] = 64;
-+ ally_cfg->deadzones[i][1][1] = 64;
-+ ally_cfg->deadzones[i][1][3] = 64;
-+ ally_cfg->response_curve[i][0][0] = 0x14;
-+ ally_cfg->response_curve[i][0][1] = 0x14;
-+ ally_cfg->response_curve[i][0][2] = 0x28;
-+ ally_cfg->response_curve[i][0][3] = 0x28;
-+ ally_cfg->response_curve[i][0][4] = 0x3c;
-+ ally_cfg->response_curve[i][0][5] = 0x3c;
-+ ally_cfg->response_curve[i][0][6] = 0x63;
-+ ally_cfg->response_curve[i][0][7] = 0x63;
-+ ally_cfg->response_curve[i][1][0] = 0x14;
-+ ally_cfg->response_curve[i][1][1] = 0x14;
-+ ally_cfg->response_curve[i][1][2] = 0x28;
-+ ally_cfg->response_curve[i][1][3] = 0x28;
-+ ally_cfg->response_curve[i][1][4] = 0x3c;
-+ ally_cfg->response_curve[i][1][5] = 0x3c;
-+ ally_cfg->response_curve[i][1][6] = 0x63;
-+ ally_cfg->response_curve[i][1][7] = 0x63;
-+ ally_cfg->vibration_intensity[i][0] = 64;
-+ ally_cfg->vibration_intensity[i][1] = 64;
-+ }
-+ drvdata.gamepad_cfg = ally_cfg;
-+
-+ /* ignore all errors for this as they are related to USB HID I/O */
-+ __gamepad_mapping_xpad_default(ally_cfg);
-+ __gamepad_mapping_wasd_default(ally_cfg);
-+ /* these calls will never error so ignore the return */
-+ __gamepad_mapping_store(ally_cfg, KB_M2, btn_pair_m1_m2, btn_pair_side_left, false);
-+ __gamepad_mapping_store(ally_cfg, KB_M1, btn_pair_m1_m2, btn_pair_side_right, false);
-+ __gamepad_set_mode(hdev, ally_cfg, xpad_mode_game);
-+ __gamepad_set_mapping(hdev, ally_cfg, btn_pair_m1_m2);
-+ /* ensure we have data for users to start from */
-+ __gamepad_get_calibration(hdev);
-+
-+ 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_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 LED control */
-+/**************************************************************************************************/
-+static void ally_schedule_work(struct ally_rgb_leds *led)
-+{
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&led->lock, flags);
-+ if (!led->removed)
-+ schedule_work(&led->work);
-+ spin_unlock_irqrestore(&led->lock, flags);
-+ pr_warn("5");
-+}
-+
-+static void ally_led_do_brightness(struct work_struct *work)
-+{
-+ struct ally_rgb_leds *led = container_of(work, struct ally_rgb_leds, work);
-+ u8 buf1[] = { FEATURE_ROG_ALLY_REPORT_ID, 0xb4, 0x00, 0x00, 0x00 };
-+ u8 buf[] = { FEATURE_ROG_ALLY_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&led->lock, flags);
-+ if (!led->update_bright) {
-+ spin_unlock_irqrestore(&led->lock, flags);
-+ return;
-+ }
-+ led->update_bright = false;
-+ buf[4] = led->brightness;
-+ spin_unlock_irqrestore(&led->lock, flags);
-+
-+ if (asus_dev_set_report(led->hdev, buf, sizeof(buf)) < 0)
-+ hid_err(led->hdev, "Ally failed to set backlight\n");
-+ if (asus_dev_set_report(led->hdev, buf1, sizeof(buf1)) < 0)
-+ hid_err(led->hdev, "Ally failed to set backlight\n");
-+}
-+
-+static void ally_led_do_rgb(struct work_struct *work)
-+{
-+ struct ally_rgb_leds *led = container_of(work, struct ally_rgb_leds, work);
-+ unsigned long flags;
-+ int ret;
-+
-+ 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[4 + i * 3] = led->gamepad_red;
-+ buf[5 + i * 3] = led->gamepad_green;
-+ buf[6 + i * 3] = led->gamepad_blue;
-+ }
-+ 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_led_work(struct work_struct *work)
-+{
-+ ally_led_do_brightness(work);
-+ ally_led_do_rgb(work);
-+}
-+
-+static void ally_backlight_set(struct led_classdev *led_cdev, enum led_brightness brightness)
-+{
-+ struct ally_rgb_leds *led =
-+ container_of(led_cdev, struct ally_rgb_leds, led_bright_dev);
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&led->lock, flags);
-+ led->update_bright = true;
-+ led->brightness = brightness;
-+ spin_unlock_irqrestore(&led->lock, flags);
-+
-+ ally_schedule_work(led);
-+}
-+
-+static enum led_brightness ally_backlight_get(struct led_classdev *led_cdev)
-+{
-+ struct ally_rgb_leds *led =
-+ container_of(led_cdev, struct ally_rgb_leds, led_bright_dev);
-+ enum led_brightness brightness;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&led->lock, flags);
-+ brightness = led->brightness;
-+ spin_unlock_irqrestore(&led->lock, flags);
-+
-+ return brightness;
-+}
-+
-+static void ally_set_rgb_brightness(struct led_classdev *cdev, enum led_brightness brightness)
-+{
-+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
-+ struct ally_rgb_leds *led = container_of(mc_cdev, struct ally_rgb_leds, led_rgb_dev);
-+ unsigned long flags;
-+
-+ led_mc_calc_color_components(mc_cdev, brightness);
-+ spin_lock_irqsave(&led->lock, flags);
-+ led->update_rgb = true;
-+ led->gamepad_red = mc_cdev->subled_info[0].brightness;
-+ led->gamepad_green = mc_cdev->subled_info[1].brightness;
-+ led->gamepad_blue = mc_cdev->subled_info[2].brightness;
-+ spin_unlock_irqrestore(&led->lock, flags);
-+
-+ ally_schedule_work(led);
-+}
-+
-+static int ally_gamepad_register_brightness(struct hid_device *hdev,
-+ struct ally_rgb_leds *led_rgb)
-+{
-+ struct led_classdev *led_cdev;
-+
-+ led_cdev = &led_rgb->led_bright_dev;
-+ led_cdev->name = "ally:kbd_backlight"; /* Let a desktop control it also */
-+ led_cdev->max_brightness = 3;
-+ led_cdev->brightness_set = ally_backlight_set;
-+ led_cdev->brightness_get = ally_backlight_get;
-+
-+ return devm_led_classdev_register(&hdev->dev, &led_rgb->led_bright_dev);
-+}
-+
-+static int ally_gamepad_register_rgb_leds(struct hid_device *hdev,
-+ struct ally_rgb_leds *led_rgb)
-+{
-+ struct mc_subled *mc_led_info;
-+ struct led_classdev *led_cdev;
-+
-+ mc_led_info = devm_kmalloc_array(&hdev->dev, 3, sizeof(*mc_led_info),
-+ GFP_KERNEL | __GFP_ZERO);
-+ if (!mc_led_info)
-+ return -ENOMEM;
-+
-+ mc_led_info[0].color_index = LED_COLOR_ID_RED;
-+ mc_led_info[1].color_index = LED_COLOR_ID_GREEN;
-+ mc_led_info[2].color_index = LED_COLOR_ID_BLUE;
-+
-+ led_rgb->led_rgb_dev.subled_info = mc_led_info;
-+ led_rgb->led_rgb_dev.num_colors = 3;
-+
-+ led_cdev = &led_rgb->led_rgb_dev.led_cdev;
-+ led_cdev->name = "ally:rgb:gamepad";
-+ led_cdev->brightness = 128;
-+ led_cdev->max_brightness = 255;
-+ led_cdev->brightness_set = ally_set_rgb_brightness;
-+
-+ return devm_led_classdev_multicolor_register(&hdev->dev, &led_rgb->led_rgb_dev);
-+}
-+
-+static struct ally_rgb_leds *ally_gamepad_rgb_create(struct hid_device *hdev)
-+{
-+ struct ally_rgb_leds *led_rgb;
-+ int ret;
-+
-+ led_rgb = devm_kzalloc(&hdev->dev, sizeof(struct ally_rgb_leds), GFP_KERNEL);
-+ if (!led_rgb)
-+ return ERR_PTR(-ENOMEM);
-+
-+ ret = ally_gamepad_register_rgb_leds(hdev, led_rgb);
-+ if (ret < 0) {
-+ cancel_work_sync(&led_rgb->work);
-+ devm_kfree(&hdev->dev, led_rgb);
-+ return ERR_PTR(ret);
-+ }
-+
-+ ret = ally_gamepad_register_brightness(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->brightness = 3;
-+ led_rgb->removed = false;
-+
-+ INIT_WORK(&led_rgb->work, ally_led_work);
-+ spin_lock_init(&led_rgb->lock);
-+
-+ return led_rgb;
-+}
-+
-+static void ally_rgb_remove(struct hid_device *hdev)
-+{
-+ struct ally_rgb_leds *led_rgb = drvdata.led_rgb;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&led_rgb->lock, flags);
-+ led_rgb->removed = true;
-+ spin_unlock_irqrestore(&led_rgb->lock, flags);
-+ cancel_work_sync(&led_rgb->work);
-+}
-+
-+/**************************************************************************************************/
-+/* ROG Ally driver init */
-+/**************************************************************************************************/
-+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) {
-+ if ((hdev->bus == BUS_USB && report->id == ALLY_X_INPUT_REPORT_USB &&
-+ size == ALLY_X_INPUT_REPORT_USB_SIZE) ||
-+ (cfg && data[0] == 0x5A)) {
-+ ally_x_raw_event(ally_x, report, data, size);
-+ } else {
-+ return -1;
-+ }
-+ }
-+ 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;
-+}
-+
-+static int ally_gamepad_init(struct hid_device *hdev, u8 report_id)
-+{
-+ const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, 0x65,
-+ 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
-+ int ret;
-+
-+ ret = asus_dev_set_report(hdev, buf, sizeof(buf));
-+ if (ret < 0)
-+ hid_err(hdev, "Ally failed to send init command: %d\n", ret);
-+
-+ return ret;
-+}
-+
-+static int ally_probe(struct hid_device *hdev, const struct hid_device_id *id)
-+{
-+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-+ struct usb_host_endpoint *ep = intf->cur_altsetting->endpoint;
-+ int ret;
-+
-+ if (ep->desc.bEndpointAddress != ALLY_CFG_INTF_IN_ADDRESS &&
-+ ep->desc.bEndpointAddress != ALLY_X_INTERFACE_ADDRESS)
-+ 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_gamepad_init(hdev, FEATURE_REPORT_ID);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ally_gamepad_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ally_gamepad_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
-+ if (ret < 0)
-+ return ret;
-+
-+ drvdata.hdev = hdev;
-+ hid_set_drvdata(hdev, &drvdata);
-+
-+ /* This should almost always exist */
-+ if (ep->desc.bEndpointAddress == ALLY_CFG_INTF_IN_ADDRESS) {
-+ drvdata.led_rgb = ally_gamepad_rgb_create(hdev);
-+ if (IS_ERR(drvdata.led_rgb))
-+ hid_err(hdev, "Failed to create Ally gamepad LEDs.\n");
-+ else
-+ hid_info(hdev, "Created Ally RGB LED controls.\n");
-+
-+ ally_gamepad_cfg_create(hdev); // assigns self
-+ 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) && IS_ERR(drvdata.gamepad_cfg))
-+ goto err_close;
-+ }
-+
-+ /* May or may not exist */
-+ if (ep->desc.bEndpointAddress == ALLY_X_INTERFACE_ADDRESS) {
-+ 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");
-+
-+ // 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;
-+
-+err_close:
-+ hid_hw_close(hdev);
-+err_stop:
-+ hid_hw_stop(hdev);
-+ return ret;
-+}
-+
-+static void ally_remove(struct hid_device *hdev)
-+{
-+ if (drvdata.ally_x)
-+ ally_x_remove(hdev);
-+ if (drvdata.led_rgb)
-+ ally_rgb_remove(hdev);
-+ if (drvdata.gamepad_cfg)
-+ ally_cfg_remove(hdev);
-+ hid_hw_close(hdev);
-+ hid_hw_stop(hdev);
-+}
-+
-+static int ally_resume(struct hid_device *hdev)
-+{
-+ int ret;
-+
-+ ret = ally_gamepad_init(hdev, FEATURE_REPORT_ID);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ally_gamepad_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ally_gamepad_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (drvdata.ally_x && drvdata.ally_x->output_worker_initialized)
-+ schedule_work(&drvdata.ally_x->output_worker);
-+
-+ return ret;
-+}
-+
-+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_probe,
-+ .remove = ally_remove,
-+ .raw_event = ally_raw_event,
-+ .resume = ally_resume,
-+};
-+
-+static int __init rog_ally_cfg_init(void)
-+{
-+ return hid_register_driver(&rog_ally_cfg);
-+}
-+
-+static void __exit rog_ally_cfg_exit(void)
-+{
-+ hid_unregister_driver(&rog_ally_cfg);
-+}
-+
-+module_init(rog_ally_cfg_init);
-+module_exit(rog_ally_cfg_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..252d9f126e32
---- /dev/null
-+++ b/drivers/hid/hid-asus-ally.h
-@@ -0,0 +1,544 @@
-+/* 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>
-+
-+#define ALLY_X_INTERFACE_ADDRESS 0x87
-+
-+#define BTN_CODE_LEN 11
-+#define MAPPING_BLOCK_LEN 44
-+
-+#define TURBO_BLOCK_LEN 32
-+#define TURBO_BLOCK_STEP 2
-+
-+#define PAD_A "pad_a"
-+#define PAD_B "pad_b"
-+#define PAD_X "pad_x"
-+#define PAD_Y "pad_y"
-+#define PAD_LB "pad_lb"
-+#define PAD_RB "pad_rb"
-+#define PAD_LS "pad_ls"
-+#define PAD_RS "pad_rs"
-+#define PAD_DPAD_UP "pad_dpad_up"
-+#define PAD_DPAD_DOWN "pad_dpad_down"
-+#define PAD_DPAD_LEFT "pad_dpad_left"
-+#define PAD_DPAD_RIGHT "pad_dpad_right"
-+#define PAD_VIEW "pad_view"
-+#define PAD_MENU "pad_menu"
-+#define PAD_XBOX "pad_xbox"
-+
-+#define KB_M1 "kb_m1"
-+#define KB_M2 "kb_m2"
-+#define KB_ESC "kb_esc"
-+#define KB_F1 "kb_f1"
-+#define KB_F2 "kb_f2"
-+#define KB_F3 "kb_f3"
-+#define KB_F4 "kb_f4"
-+#define KB_F5 "kb_f5"
-+#define KB_F6 "kb_f6"
-+#define KB_F7 "kb_f7"
-+#define KB_F8 "kb_f8"
-+#define KB_F9 "kb_f9"
-+#define KB_F10 "kb_f10"
-+#define KB_F11 "kb_f11"
-+#define KB_F12 "kb_f12"
-+#define KB_F14 "kb_f14"
-+#define KB_F15 "kb_f15"
-+
-+#define KB_BACKTICK "kb_backtick"
-+#define KB_1 "kb_1"
-+#define KB_2 "kb_2"
-+#define KB_3 "kb_3"
-+#define KB_4 "kb_4"
-+#define KB_5 "kb_5"
-+#define KB_6 "kb_6"
-+#define KB_7 "kb_7"
-+#define KB_8 "kb_8"
-+#define KB_9 "kb_9"
-+#define KB_0 "kb_0"
-+#define KB_HYPHEN "kb_hyphen"
-+#define KB_EQUALS "kb_equals"
-+#define KB_BACKSPACE "kb_backspace"
-+
-+#define KB_TAB "kb_tab"
-+#define KB_Q "kb_q"
-+#define KB_W "kb_w"
-+#define KB_E "kb_e"
-+#define KB_R "kb_r"
-+#define KB_T "kb_t"
-+#define KB_Y "kb_y"
-+#define KB_U "kb_u"
-+#define KB_I "kb_i"
-+#define KB_O "kb_o"
-+#define KB_P "kb_p"
-+#define KB_LBRACKET "kb_lbracket"
-+#define KB_RBRACKET "kb_rbracket"
-+#define KB_BACKSLASH "kb_bkslash"
-+
-+#define KB_CAPS "kb_caps"
-+#define KB_A "kb_a"
-+#define KB_S "kb_s"
-+#define KB_D "kb_d"
-+#define KB_F "kb_f"
-+#define KB_G "kb_g"
-+#define KB_H "kb_h"
-+#define KB_J "kb_j"
-+#define KB_K "kb_k"
-+#define KB_L "kb_l"
-+#define KB_SEMI "kb_semicolon"
-+#define KB_QUOTE "kb_quote"
-+#define KB_RET "kb_enter"
-+
-+#define KB_LSHIFT "kb_lshift"
-+#define KB_Z "kb_z"
-+#define KB_X "kb_x"
-+#define KB_C "kb_c"
-+#define KB_V "kb_v"
-+#define KB_B "kb_b"
-+#define KB_N "kb_n"
-+#define KB_M "kb_m"
-+#define KB_COMMA "kb_comma"
-+#define KB_PERIOD "kb_period"
-+#define KB_FWDSLASH "kb_fwdslash"
-+#define KB_RSHIFT "kb_rshift"
-+
-+#define KB_LCTL "kb_lctl"
-+#define KB_META "kb_meta"
-+#define KB_LALT "kb_lalt"
-+#define KB_SPACE "kb_space"
-+#define KB_RALT "kb_ralt"
-+#define KB_MENU "kb_menu"
-+#define KB_RCTL "kb_rctl"
-+
-+#define KB_PRNTSCN "kb_prntscn"
-+#define KB_SCRLCK "kb_scrlck"
-+#define KB_PAUSE "kb_pause"
-+#define KB_INS "kb_ins"
-+#define KB_HOME "kb_home"
-+#define KB_PGUP "kb_pgup"
-+#define KB_DEL "kb_del"
-+#define KB_END "kb_end"
-+#define KB_PGDWN "kb_pgdwn"
-+
-+#define KB_UP_ARROW "kb_up_arrow"
-+#define KB_DOWN_ARROW "kb_down_arrow"
-+#define KB_LEFT_ARROW "kb_left_arrow"
-+#define KB_RIGHT_ARROW "kb_right_arrow"
-+
-+#define NUMPAD_LOCK "numpad_lock"
-+#define NUMPAD_FWDSLASH "numpad_fwdslash"
-+#define NUMPAD_ASTERISK "numpad_asterisk"
-+#define NUMPAD_HYPHEN "numpad_hyphen"
-+#define NUMPAD_0 "numpad_0"
-+#define NUMPAD_1 "numpad_1"
-+#define NUMPAD_2 "numpad_2"
-+#define NUMPAD_3 "numpad_3"
-+#define NUMPAD_4 "numpad_4"
-+#define NUMPAD_5 "numpad_5"
-+#define NUMPAD_6 "numpad_6"
-+#define NUMPAD_7 "numpad_7"
-+#define NUMPAD_8 "numpad_8"
-+#define NUMPAD_9 "numpad_9"
-+#define NUMPAD_PLUS "numpad_plus"
-+#define NUMPAD_ENTER "numpad_enter"
-+#define NUMPAD_PERIOD "numpad_."
-+
-+#define MOUSE_LCLICK "rat_lclick"
-+#define MOUSE_RCLICK "rat_rclick"
-+#define MOUSE_MCLICK "rat_mclick"
-+#define MOUSE_WHEEL_UP "rat_wheel_up"
-+#define MOUSE_WHEEL_DOWN "rat_wheel_down"
-+
-+#define MEDIA_SCREENSHOT "media_screenshot"
-+#define MEDIA_SHOW_KEYBOARD "media_show_keyboard"
-+#define MEDIA_SHOW_DESKTOP "media_show_desktop"
-+#define MEDIA_START_RECORDING "media_start_recording"
-+#define MEDIA_MIC_OFF "media_mic_off"
-+#define MEDIA_VOL_DOWN "media_vol_down"
-+#define MEDIA_VOL_UP "media_vol_up"
-+
-+/* 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)
-+
-+#define ALLY_DEVICE_ATTR_RO(_name, _sysfs_name) \
-+ struct device_attribute dev_attr_##_name = \
-+ __ATTR(_sysfs_name, 0444, _name##_show, NULL)
-+
-+#define ALLY_DEVICE_ATTR_WO(_name, _sysfs_name) \
-+ struct device_attribute dev_attr_##_name = \
-+ __ATTR(_sysfs_name, 0200, NULL, _name##_store)
-+
-+/* response curve macros */
-+#define ALLY_RESP_CURVE_SHOW(_name, _point_n) \
-+ static ssize_t _name##_show(struct device *dev, \
-+ struct device_attribute *attr, char *buf) \
-+ { \
-+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
-+ int idx = (_point_n - 1) * 2; \
-+ if (!drvdata.gamepad_cfg) \
-+ return -ENODEV; \
-+ return sysfs_emit( \
-+ buf, "%d %d\n", \
-+ ally_cfg->response_curve[ally_cfg->mode] \
-+ [btn_pair_side_left][idx], \
-+ ally_cfg->response_curve[ally_cfg->mode] \
-+ [btn_pair_side_right] \
-+ [idx + 1]); \
-+ }
-+
-+#define ALLY_RESP_CURVE_STORE(_name, _side, _point_n) \
-+ static ssize_t _name##_store(struct device *dev, \
-+ struct device_attribute *attr, \
-+ const char *buf, size_t count) \
-+ { \
-+ int ret = __gamepad_store_response_curve( \
-+ dev, buf, btn_pair_side_##_side, _point_n); \
-+ if (ret < 0) \
-+ return ret; \
-+ return count; \
-+ }
-+
-+/* _point_n must start at 1 */
-+#define ALLY_JS_RC_POINT(_side, _point_n, _sysfs_label) \
-+ ALLY_RESP_CURVE_SHOW(rc_point_##_side##_##_point_n, _point_n); \
-+ ALLY_RESP_CURVE_STORE(rc_point_##_side##_##_point_n, _side, _point_n); \
-+ ALLY_DEVICE_ATTR_RW(rc_point_##_side##_##_point_n, \
-+ _sysfs_label##_point_n)
-+
-+/* deadzone macros */
-+#define ALLY_AXIS_DEADZONE_SHOW(_axis) \
-+ static ssize_t _axis##_deadzone_show( \
-+ struct device *dev, struct device_attribute *attr, char *buf) \
-+ { \
-+ struct ally_gamepad_cfg *ally_cfg = drvdata.gamepad_cfg; \
-+ int side, is_tr; \
-+ if (!drvdata.gamepad_cfg) \
-+ return -ENODEV; \
-+ is_tr = _axis > xpad_axis_xy_right; \
-+ side = _axis == xpad_axis_xy_right || \
-+ _axis == xpad_axis_z_right ? \
-+ 2 : \
-+ 0; \
-+ return sysfs_emit( \
-+ buf, "%d %d\n", \
-+ ally_cfg->deadzones[ally_cfg->mode][is_tr][side], \
-+ ally_cfg->deadzones[ally_cfg->mode][is_tr][side + 1]); \
-+ }
-+
-+#define ALLY_AXIS_DEADZONE_STORE(_axis) \
-+ static ssize_t _axis##_deadzone_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; \
-+ int ret = __gamepad_store_deadzones(ally_cfg, _axis, buf); \
-+ if (ret < 0) \
-+ return ret; \
-+ return count; \
-+ }
-+
-+#define ALLY_AXIS_DEADZONE(_axis, _sysfs_label) \
-+ ALLY_AXIS_DEADZONE_SHOW(_axis); \
-+ ALLY_AXIS_DEADZONE_STORE(_axis); \
-+ ALLY_DEVICE_ATTR_RW(_axis##_deadzone, _sysfs_label)
-+
-+/* button specific macros */
-+#define ALLY_BTN_SHOW(_fname, _pair, _side, _secondary) \
-+ 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, "%s\n", \
-+ __btn_map_to_string(ally_cfg, _pair, _side, \
-+ _secondary)); \
-+ }
-+
-+#define ALLY_BTN_STORE(_fname, _pair, _side, _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; \
-+ if (!drvdata.gamepad_cfg) \
-+ return -ENODEV; \
-+ int ret = __gamepad_mapping_store(ally_cfg, buf, _pair, _side, \
-+ _secondary); \
-+ if (ret < 0) \
-+ return ret; \
-+ return count; \
-+ }
-+
-+#define ALLY_BTN_TURBO_SHOW(_fname, _pair, _side) \
-+ static ssize_t _fname##_turbo_show( \
-+ struct device *dev, struct device_attribute *attr, char *buf) \
-+ { \
-+ return sysfs_emit(buf, "%d\n", \
-+ __gamepad_turbo_show(dev, _pair, _side)); \
-+ }
-+
-+#define ALLY_BTN_TURBO_STORE(_fname, _pair, _side) \
-+ static ssize_t _fname##_turbo_store(struct device *dev, \
-+ struct device_attribute *attr, \
-+ const char *buf, size_t count) \
-+ { \
-+ int ret = __gamepad_turbo_store(dev, buf, _pair, _side); \
-+ if (ret < 0) \
-+ return ret; \
-+ return count; \
-+ }
-+
-+#define ALLY_BTN_ATTRS_GROUP(_name, _fname) \
-+ static struct attribute *_fname##_attrs[] = { \
-+ &dev_attr_##_fname.attr, &dev_attr_##_fname##_macro.attr, \
-+ &dev_attr_##_fname##_turbo.attr, NULL \
-+ }; \
-+ static const struct attribute_group _fname##_attr_group = { \
-+ .name = __stringify(_name), \
-+ .attrs = _fname##_attrs, \
-+ }
-+
-+#define ALLY_BTN_MAPPING(_fname, _pair, _side) \
-+ ALLY_BTN_SHOW(btn_mapping_##_fname, _pair, _side, false); \
-+ ALLY_BTN_STORE(btn_mapping_##_fname, _pair, _side, false); \
-+ ALLY_BTN_SHOW(btn_mapping_##_fname##_macro, _pair, _side, true); \
-+ ALLY_BTN_STORE(btn_mapping_##_fname##_macro, _pair, _side, true); \
-+ ALLY_BTN_TURBO_SHOW(btn_mapping_##_fname, _pair, _side); \
-+ ALLY_BTN_TURBO_STORE(btn_mapping_##_fname, _pair, _side); \
-+ ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname, remap); \
-+ ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname##_macro, macro_remap); \
-+ ALLY_DEVICE_ATTR_RW(btn_mapping_##_fname##_turbo, turbo); \
-+ ALLY_BTN_ATTRS_GROUP(btn_##_fname, btn_mapping_##_fname)
-+
-+/* calibration macros */
-+#define ALLY_CAL_STORE(_fname, _axis) \
-+ static ssize_t _fname##_store(struct device *dev, \
-+ struct device_attribute *attr, \
-+ const char *buf, size_t count) \
-+ { \
-+ int ret = __gamepad_cal_store(dev, buf, _axis); \
-+ if (ret < 0) \
-+ return ret; \
-+ return count; \
-+ }
-+
-+#define ALLY_CAL_SHOW(_fname, _axis) \
-+ static ssize_t _fname##_show(struct device *dev, \
-+ struct device_attribute *attr, char *buf) \
-+ { \
-+ return __gamepad_cal_show(dev, buf, _axis); \
-+ }
-+
-+#define ALLY_CAL_ATTR(_fname, _axis, _sysfs_label) \
-+ ALLY_CAL_STORE(_fname, _axis); \
-+ ALLY_CAL_SHOW(_fname, _axis); \
-+ ALLY_DEVICE_ATTR_RW(_fname, _sysfs_label)
-+
-+#define ALLY_CAL_RESET_STORE(_fname, _axis) \
-+ static ssize_t _fname##_store(struct device *dev, \
-+ struct device_attribute *attr, \
-+ const char *buf, size_t count) \
-+ { \
-+ int ret = __gamepad_cal_reset(dev, buf, _axis); \
-+ if (ret < 0) \
-+ return ret; \
-+ return count; \
-+ }
-+
-+#define ALLY_CAL_RESET_ATTR(_fname, _axis, _sysfs_label) \
-+ ALLY_CAL_RESET_STORE(_fname, _axis); \
-+ ALLY_DEVICE_ATTR_WO(_fname, _sysfs_label)
-+
-+/*
-+ * The following blocks of packets exist to make setting a default boot config
-+ * easier. They were directly captured from setting the gamepad up.
-+ */
-+
-+/* Default blocks for the xpad mode */
-+static const u8 XPAD_DEF1[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x05, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8c, 0x88, 0x76, 0x00, 0x00
-+};
-+static const u8 XPAD_DEF2[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x23, 0x00, 0x00, 0x00,
-+ 0x01, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x0d, 0x00, 0x00, 0x00
-+};
-+static const u8 XPAD_DEF3[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 XPAD_DEF4[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 XPAD_DEF5[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x05, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x31, 0x00, 0x00, 0x00
-+};
-+static const u8 XPAD_DEF6[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x4d, 0x00, 0x00, 0x00,
-+ 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x05, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 XPAD_DEF7[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 XPAD_DEF8[MAPPING_BLOCK_LEN] = {
-+ 0x02, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 XPAD_DEF9[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+
-+/* default blocks for the wasd mode */
-+static const u8 WASD_DEF1[MAPPING_BLOCK_LEN] = {
-+ 0x02, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x05, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8c, 0x88, 0x76, 0x00, 0x00
-+};
-+static const u8 WASD_DEF2[MAPPING_BLOCK_LEN] = {
-+ 0x02, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x23, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x0d, 0x00, 0x00, 0x00
-+};
-+static const u8 WASD_DEF3[MAPPING_BLOCK_LEN] = {
-+ 0x02, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 WASD_DEF4[MAPPING_BLOCK_LEN] = {
-+ 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 WASD_DEF5[MAPPING_BLOCK_LEN] = {
-+ 0x02, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x05, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x31, 0x00, 0x00, 0x00
-+};
-+static const u8 WASD_DEF6[MAPPING_BLOCK_LEN] = {
-+ 0x02, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x82, 0x4d, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x05, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 WASD_DEF7[MAPPING_BLOCK_LEN] = {
-+ 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x01, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 WASD_DEF8[MAPPING_BLOCK_LEN] = {
-+ 0x02, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x02, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+static const u8 WASD_DEF9[MAPPING_BLOCK_LEN] = {
-+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x88, 0x0d, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-+};
-+
-+/*
-+ * 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,
-+};
-+
-+/*
-+ * the xpad_mode is used in various set and query HID packets and is
-+ * used for indexing (xpad_axis - 1)
-+ */
-+enum xpad_axis {
-+ xpad_axis_xy_left = 0x01,
-+ xpad_axis_xy_right = 0x02,
-+ xpad_axis_z_left = 0x03,
-+ xpad_axis_z_right = 0x04,
-+};
-+
-+enum btn_pair {
-+ 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,
-+};
-+
-+enum btn_pair_side {
-+ btn_pair_side_left = 0x00,
-+ btn_pair_side_right = 0x01,
-+};
-diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
-index 37e6d25593c2..9f83a770de8d 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 ALLY_CFG_INTF_IN_ADDRESS 0x83
-+#define ALLY_CFG_INTF_OUT_ADDRESS 0x04
-+#define ALLY_X_INTERFACE_ADDRESS 0x87
-+
- #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 | \
-@@ -492,12 +497,19 @@ static void asus_kbd_backlight_work(struct work_struct *work)
- */
- static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
- {
-+ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
- u32 value;
- int ret;
-
- if (!IS_ENABLED(CONFIG_ASUS_WMI))
- return false;
-
-+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD &&
-+ dmi_check_system(asus_use_hid_led_dmi_ids)) {
-+ hid_info(hdev, "using HID for asus::kbd_backlight\n");
-+ return false;
-+ }
-+
- ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
- ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
- hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
-@@ -996,6 +1008,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 will be used by other drivers */
-+ 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 == ALLY_X_INTERFACE_ADDRESS ||
-+ ep->desc.bEndpointAddress == ALLY_CFG_INTF_IN_ADDRESS ||
-+ ep->desc.bEndpointAddress == ALLY_CFG_INTF_OUT_ADDRESS)
-+ return -ENODEV;
-+ }
-+
- /*
- * T90CHI's keyboard dock returns same ID values as T100CHI's dock.
- * Thus, identify T90CHI dock with product name string.
-@@ -1247,6 +1270,12 @@ 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_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_ROG_ALLY_XPAD },
-+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
-+ USB_DEVICE_ID_ASUSTEK_ROG_AZOTH_KEYBOARD),
- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
- { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
- USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD),
-diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
-index 72d56ee7ce1b..57f1f1bc5eb6 100644
---- a/drivers/hid/hid-ids.h
-+++ b/drivers/hid/hid-ids.h
-@@ -209,7 +209,10 @@
- #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6
- #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 0x1a30
- #define USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR 0x18c6
-+#define USB_DEVICE_ID_ASUSTEK_ROG_RAIKIRI_PAD 0x1abb
- #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY 0x1abe
-+#define USB_DEVICE_ID_ASUSTEK_ROG_AZOTH_KEYBOARD 0x1a83
-+#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X 0x1b4c
- #define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b
- #define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
-
-diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
-index ddfccc226751..d13c4085c228 100644
---- a/drivers/platform/x86/Kconfig
-+++ b/drivers/platform/x86/Kconfig
-@@ -265,6 +265,19 @@ 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 (firmware) Driver"
-+ depends on ACPI_WMI
-+ depends on ASUS_WMI
-+ select FW_ATTR_CLASS
-+ help
-+ Say Y here if you have a WMI aware Asus laptop 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
-@@ -276,6 +289,7 @@ config ASUS_WMI
- depends on HOTPLUG_PCI
- depends on ACPI_VIDEO || ACPI_VIDEO = n
- depends on SERIO_I8042 || SERIO_I8042 = n
-+ select ASUS_ARMOURY
- select INPUT_SPARSEKMAP
- select LEDS_CLASS
- select NEW_LEDS
-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/amd/pmf/pmf-quirks.c b/drivers/platform/x86/amd/pmf/pmf-quirks.c
-index 0b2eb0ae85fe..460444cda1b2 100644
---- a/drivers/platform/x86/amd/pmf/pmf-quirks.c
-+++ b/drivers/platform/x86/amd/pmf/pmf-quirks.c
-@@ -29,6 +29,14 @@ static const struct dmi_system_id fwbug_list[] = {
- },
- .driver_data = &quirk_no_sps_bug,
- },
-+ {
-+ .ident = "ROG Ally X",
-+ .matches = {
-+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
-+ DMI_MATCH(DMI_PRODUCT_NAME, "RC72LA"),
-+ },
-+ .driver_data = &quirk_no_sps_bug,
-+ },
- {}
- };
-
-@@ -48,4 +56,3 @@ void amd_pmf_quirks_init(struct amd_pmf_dev *dev)
- dmi_id->ident);
- }
- }
--
-diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
-new file mode 100644
-index 000000000000..aa784b6b9483
---- /dev/null
-+++ b/drivers/platform/x86/asus-armoury.c
-@@ -0,0 +1,1049 @@
-+// 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) 2010 Intel Corporation.
-+ * Copyright(C) 2024-2024 Luke Jones <luke@ljones.dev>
-+ */
-+
-+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-+
-+#include <linux/platform_data/x86/asus-wmi.h>
-+#include <linux/errno.h>
-+#include <linux/fs.h>
-+#include <linux/types.h>
-+#include <linux/dmi.h>
-+#include <linux/device.h>
-+#include <linux/kmod.h>
-+#include <linux/kobject.h>
-+#include <linux/module.h>
-+#include <linux/mutex.h>
-+#include <linux/kernel.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
-+/* New mode on some devices, define here to clarify remapping later */
-+#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
-+
-+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,
-+};
-+
-+/* Default limits for tunables available on ASUS ROG laptops */
-+#define PPT_CPU_LIMIT_MIN 5
-+#define PPT_CPU_LIMIT_MAX 150
-+#define PPT_CPU_LIMIT_DEFAULT 80
-+#define PPT_PLATFORM_MIN 5
-+#define PPT_PLATFORM_MAX 100
-+#define PPT_PLATFORM_DEFAULT 80
-+#define NVIDIA_BOOST_MIN 5
-+#define NVIDIA_BOOST_MAX 25
-+#define NVIDIA_TEMP_MIN 75
-+#define NVIDIA_TEMP_MAX 87
-+#define NVIDIA_POWER_MIN 0
-+#define NVIDIA_POWER_MAX 70
-+#define NVIDIA_POWER_DEFAULT 70
-+
-+/* Tunables provided by ASUS for gaming laptops */
-+struct rog_tunables {
-+ u32 cpu_default;
-+ u32 cpu_min;
-+ u32 cpu_max;
-+
-+ u32 platform_default;
-+ u32 platform_min;
-+ u32 platform_max;
-+
-+ u32 ppt_pl1_spl; // cpu
-+ u32 ppt_pl2_sppt; // cpu
-+ u32 ppt_apu_sppt; // plat
-+ u32 ppt_platform_sppt; // plat
-+ u32 ppt_fppt; // cpu
-+
-+ u32 nv_boost_default;
-+ u32 nv_boost_min;
-+ u32 nv_boost_max;
-+ u32 nv_dynamic_boost;
-+
-+ u32 nv_temp_default;
-+ u32 nv_temp_min;
-+ u32 nv_temp_max;
-+ u32 nv_temp_target;
-+
-+ u32 dgpu_tgp_default;
-+ u32 dgpu_tgp_min;
-+ u32 dgpu_tgp_max;
-+ u32 dgpu_tgp;
-+
-+ u32 min_perf_cores;
-+ u32 max_perf_cores;
-+ u32 max_power_cores;
-+};
-+
-+static const struct class *fw_attr_class;
-+
-+struct asus_armoury_priv {
-+ struct device *fw_attr_dev;
-+ struct kset *fw_attr_kset;
-+
-+ struct rog_tunables *rog_tunables;
-+ 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 {
-+ u32 pending_reboot;
-+};
-+
-+static struct fw_attrs_group fw_attrs = {
-+ .pending_reboot = 0,
-+};
-+
-+struct asus_attr_group {
-+ const struct attribute_group *attr_group;
-+ u32 wmi_devid;
-+};
-+
-+/**
-+ * asus_wmi_is_present() - determine if a WMI interface is available.
-+ * @dev_id: The WMI function ID to use.
-+ *
-+ * Returns: Boolean state. Note that an error will also return false.
-+ */
-+static bool asus_wmi_is_present(u32 dev_id)
-+{
-+ u32 retval;
-+ int status;
-+
-+ status = asus_wmi_get_devstate_dsts(dev_id, &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 = 1;
-+ 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, "%u\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") ||
-+ !strcmp(attr->attr.name, "cores_performance") ||
-+ !strcmp(attr->attr.name, "cores_efficiency") ||
-+ !strcmp(attr->attr.name, "panel_hd_mode");
-+}
-+
-+/**
-+ * attr_int_store() - Generic store function for use with most WMI functions.
-+ * @kobj: Pointer to the driver object.
-+ * @kobj_attribute: Pointer the the attribute calling this function.
-+ * @buf: The buffer to read from, this is parsed to `int` type.
-+ * @count:
-+ * @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.
-+ *
-+ * The WMI functions available on most ASUS laptops return a 1 as "success", and
-+ * a 0 as failed. However some functions can return n > 1 for additional errors.
-+ * attr_int_store() currently treats all values which are not 1 as errors, ignoring
-+ * the possible differences in WMI error returns.
-+ *
-+ * Returns: Either count, or an error.
-+ */
-+static ssize_t attr_int_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 result, value;
-+ int err;
-+
-+ err = kstrtouint(buf, 10, &value);
-+ if (err)
-+ return err;
-+
-+ if (value < min || value > max)
-+ return -EINVAL;
-+
-+ 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;
-+ }
-+
-+ if (result != 1) {
-+ pr_err("Failed to set %s (result): 0x%x\n", attr->attr.name, result);
-+ return -EIO;
-+ }
-+
-+ 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 = 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)
-+{
-+ int result, err;
-+ u32 mode;
-+
-+ 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 = asus_wmi_set_devstate(asus_armoury.mini_led_dev_id, mode, &result);
-+ if (err) {
-+ pr_warn("Failed to set mini-LED: %d\n", err);
-+ return err;
-+ }
-+
-+ if (result != 1) {
-+ pr_warn("Failed to set mini-LED mode (result): 0x%x\n", result);
-+ return -EIO;
-+ }
-+
-+ 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: %d\n", 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 = asus_wmi_set_devstate(asus_armoury.gpu_mux_dev_id, optimus, &result);
-+ if (err) {
-+ pr_err("Failed to set GPU MUX mode: %d\nn", err);
-+ return err;
-+ }
-+ /* !1 is considered a fail by ASUS */
-+ if (result != 1) {
-+ pr_warn("Failed to set GPU MUX mode (result): 0x%x\n", result);
-+ return -EIO;
-+ }
-+
-+ sysfs_notify(kobj, NULL, attr->attr.name);
-+
-+ 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, typcial store first, then
-+ * rescan PCI bus to activate power, then store a second time to save correctly.
-+ * The reason for this is that an extra code path in the ACPI is enabled when
-+ * the device and bus are powered.
-+ */
-+static ssize_t dgpu_disable_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;
-+ }
-+ }
-+
-+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, disable, &result);
-+ if (err) {
-+ pr_warn("Failed to set dgpu disable: %d\n", err);
-+ return err;
-+ }
-+
-+ if (result != 1) {
-+ pr_warn("Failed to set dgpu disable (result): 0x%x\n", result);
-+ return -EIO;
-+ }
-+
-+ 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 = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, enable, &result);
-+ if (err) {
-+ pr_warn("Failed to set egpu state: %d\n", err);
-+ return err;
-+ }
-+
-+ if (result != 1) {
-+ pr_warn("Failed to set egpu state (retval): 0x%x\n", result);
-+ return -EIO;
-+ }
-+
-+ 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)");
-+
-+/* 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 256:
-+ mem = 0;
-+ break;
-+ case 258:
-+ mem = 1;
-+ break;
-+ case 259:
-+ mem = 2;
-+ break;
-+ case 260:
-+ mem = 3;
-+ break;
-+ case 261:
-+ mem = 4;
-+ break;
-+ case 262:
-+ /* This is out of order and looks wrong but is correct */
-+ mem = 8;
-+ break;
-+ case 263:
-+ mem = 5;
-+ break;
-+ case 264:
-+ mem = 6;
-+ break;
-+ case 265:
-+ mem = 7;
-+ break;
-+ default:
-+ mem = 4;
-+ break;
-+ }
-+
-+ return sysfs_emit(buf, "%d\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 = 0;
-+ break;
-+ case 1:
-+ mem = 258;
-+ break;
-+ case 2:
-+ mem = 259;
-+ break;
-+ case 3:
-+ mem = 260;
-+ break;
-+ case 4:
-+ mem = 261;
-+ break;
-+ case 5:
-+ mem = 263;
-+ break;
-+ case 6:
-+ mem = 264;
-+ break;
-+ case 7:
-+ mem = 265;
-+ break;
-+ case 8:
-+ /* This is outof order and looks wrong but is correct */
-+ mem = 262;
-+ 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 %dGB, 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 the available system memory for the APU to use");
-+
-+static int init_max_cpu_cores(void)
-+{
-+ u32 cores;
-+ int err;
-+
-+ asus_armoury.rog_tunables->min_perf_cores = 4;
-+ asus_armoury.rog_tunables->max_perf_cores = 4;
-+ asus_armoury.rog_tunables->max_power_cores = 8;
-+
-+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES_MAX, &cores);
-+ if (err)
-+ return err;
-+
-+ cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
-+ asus_armoury.rog_tunables->max_power_cores = (cores & 0xff00) >> 8;
-+ asus_armoury.rog_tunables->max_perf_cores = cores & 0xff;
-+
-+ 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;
-+ int err;
-+
-+ 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.rog_tunables->max_perf_cores);
-+ else
-+ return sysfs_emit(buf, "%d\n", asus_armoury.rog_tunables->max_power_cores);
-+ case CPU_CORE_MIN:
-+ if (core_type == CPU_CORE_PERF)
-+ return sysfs_emit(buf, "%d\n", asus_armoury.rog_tunables->min_perf_cores);
-+ else
-+ return sysfs_emit(buf, "%d\n", 0);
-+ default:
-+ break;
-+ }
-+
-+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, &cores);
-+ if (err)
-+ return err;
-+
-+ cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
-+ if (core_type == CPU_CORE_PERF)
-+ cores &= 0xff;
-+ else
-+ cores = (cores & 0xff00) >> 8;
-+ 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)
-+{
-+ int result, err;
-+ u32 cores, currentv, min, max;
-+
-+ result = kstrtou32(buf, 10, &cores);
-+ if (result)
-+ return result;
-+
-+ if (core_type == CPU_CORE_PERF) {
-+ min = asus_armoury.rog_tunables->min_perf_cores;
-+ max = asus_armoury.rog_tunables->max_perf_cores;
-+ } else {
-+ min = 0;
-+ max = asus_armoury.rog_tunables->max_power_cores;
-+ }
-+ if (cores < min || cores > max)
-+ return -EINVAL;
-+
-+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, &currentv);
-+ if (err)
-+ return err;
-+
-+ if (core_type == CPU_CORE_PERF)
-+ cores |= (currentv & 0xff00);
-+ else
-+ cores |= currentv & 0xff;
-+
-+ if (cores == currentv)
-+ return 0;
-+
-+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_CORES, cores, &result);
-+ 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_ROG_TUNABLE(ppt_pl1_spl, "ppt_pl1_spl", ASUS_WMI_DEVID_PPT_PL1_SPL,
-+ cpu_default, cpu_min, cpu_max, 1, "Set the CPU slow package limit");
-+ATTR_GROUP_ROG_TUNABLE(ppt_pl2_sppt, "ppt_pl2_sppt", ASUS_WMI_DEVID_PPT_PL2_SPPT,
-+ cpu_default, cpu_min, cpu_max, 1, "Set the CPU fast package limit");
-+ATTR_GROUP_ROG_TUNABLE(ppt_apu_sppt, "ppt_apu_sppt", ASUS_WMI_DEVID_PPT_APU_SPPT,
-+ platform_default, platform_min, platform_max, 1, "Set the CPU slow package limit");
-+ATTR_GROUP_ROG_TUNABLE(ppt_platform_sppt, "ppt_platform_sppt", ASUS_WMI_DEVID_PPT_PLAT_SPPT,
-+ platform_default, platform_min, platform_max, 1, "Set the CPU slow package limit");
-+ATTR_GROUP_ROG_TUNABLE(ppt_fppt, "ppt_fppt", ASUS_WMI_DEVID_PPT_FPPT,
-+ cpu_default, cpu_min, cpu_max, 1, "Set the CPU slow package limit");
-+
-+ATTR_GROUP_ROG_TUNABLE(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BOOST,
-+ nv_boost_default, nv_boost_min, nv_boost_max, 1, "Set the Nvidia dynamic boost limit");
-+ATTR_GROUP_ROG_TUNABLE(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
-+ nv_temp_default, nv_boost_min, nv_temp_max, 1, "Set the Nvidia max thermal limit");
-+ATTR_GROUP_INT_VALUE_ONLY_RO(dgpu_base_tgp, "dgpu_base_tgp", ASUS_WMI_DEVID_DGPU_BASE_TGP,
-+ "Read the base TGP value");
-+ATTR_GROUP_ROG_TUNABLE(dgpu_tgp, "dgpu_tgp", ASUS_WMI_DEVID_DGPU_SET_TGP,
-+ dgpu_tgp_default, dgpu_tgp_min, dgpu_tgp_max, 1,
-+ "Set the additional TGP on top of the base TGP");
-+
-+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_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");
-+
-+/* 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 },
-+
-+ { &ppt_pl1_spl_attr_group, ASUS_WMI_DEVID_PPT_PL1_SPL },
-+ { &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_PPT_PL2_SPPT },
-+ { &ppt_apu_sppt_attr_group, ASUS_WMI_DEVID_PPT_APU_SPPT },
-+ { &ppt_platform_sppt_attr_group, ASUS_WMI_DEVID_PPT_PLAT_SPPT },
-+ { &ppt_fppt_attr_group, ASUS_WMI_DEVID_PPT_FPPT },
-+ { &nv_dynamic_boost_attr_group, ASUS_WMI_DEVID_NV_DYN_BOOST },
-+ { &nv_temp_target_attr_group, ASUS_WMI_DEVID_NV_THERM_TARGET },
-+ { &dgpu_base_tgp_attr_group, ASUS_WMI_DEVID_DGPU_BASE_TGP },
-+ { &dgpu_tgp_attr_group, ASUS_WMI_DEVID_DGPU_SET_TGP },
-+ { &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 },
-+ { &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)
-+{
-+ int err;
-+
-+ err = fw_attributes_class_get(&fw_attr_class);
-+ if (err)
-+ goto fail_class_created;
-+
-+ 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);
-+ goto fail_class_created;
-+ }
-+
-+ asus_armoury.fw_attr_kset = kset_create_and_add("attributes", NULL,
-+ &asus_armoury.fw_attr_dev->kobj);
-+ if (!asus_armoury.fw_attr_dev) {
-+ err = -ENOMEM;
-+ pr_debug("Failed to create and add attributes\n");
-+ goto err_destroy_classdev;
-+ }
-+
-+ err = sysfs_create_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
-+ if (err) {
-+ pr_warn("Failed to create sysfs level attributes\n");
-+ goto fail_class_created;
-+ }
-+
-+ err = 0;
-+ 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;
-+ err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
-+ &mini_led_mode_attr_group);
-+ } else if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE2)) {
-+ asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
-+ err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
-+ &mini_led_mode_attr_group);
-+ }
-+ if (err)
-+ pr_warn("Failed to create sysfs-group for mini_led\n");
-+
-+ err = 0;
-+ 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;
-+ err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
-+ } else if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX_VIVO)) {
-+ asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO;
-+ err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
-+ }
-+ if (err)
-+ pr_warn("Failed to create sysfs-group for gpu_mux\n");
-+
-+ for (int 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_warn("Failed to create sysfs-group for %s\n",
-+ armoury_attr_groups[i].attr_group->name);
-+ else
-+ pr_debug("Created sysfs-group for %s\n",
-+ armoury_attr_groups[i].attr_group->name);
-+ }
-+
-+ return 0;
-+
-+err_destroy_classdev:
-+ device_destroy(fw_attr_class, MKDEV(0, 0));
-+
-+fail_class_created:
-+ fw_attributes_class_put();
-+ return err;
-+}
-+
-+/* Init / exit ****************************************************************/
-+
-+/* Set up the min/max and defaults for ROG tunables */
-+static void init_rog_tunables(struct rog_tunables *rog)
-+{
-+ const char *product;
-+ u32 max_boost = NVIDIA_BOOST_MAX;
-+ u32 cpu_default = PPT_CPU_LIMIT_DEFAULT;
-+ u32 cpu_max = PPT_CPU_LIMIT_MAX;
-+ u32 platform_default = PPT_PLATFORM_DEFAULT;
-+ u32 platform_max = PPT_PLATFORM_MAX;
-+
-+ /*
-+ * ASUS product_name contains everything required, e.g,
-+ * "ROG Flow X16 GV601VV_GV601VV_00185149B"
-+ */
-+ product = dmi_get_system_info(DMI_PRODUCT_NAME);
-+
-+ if (strstr(product, "GA402R")) {
-+ cpu_default = 125;
-+ } else if (strstr(product, "13QY")) {
-+ cpu_max = 250;
-+ } else if (strstr(product, "X13")) {
-+ cpu_max = 75;
-+ cpu_default = 50;
-+ } else if (strstr(product, "RC71")) {
-+ cpu_max = 50;
-+ cpu_default = 30;
-+ } else if (strstr(product, "G814")
-+ || strstr(product, "G614")
-+ || strstr(product, "G834")
-+ || strstr(product, "G634")) {
-+ cpu_max = 175;
-+ } else if (strstr(product, "GA402X")
-+ || strstr(product, "GA403")
-+ || strstr(product, "FA507N")
-+ || strstr(product, "FA507X")
-+ || strstr(product, "FA707N")
-+ || strstr(product, "FA707X")) {
-+ cpu_max = 90;
-+ }
-+
-+ if (strstr(product, "GZ301ZE"))
-+ max_boost = 5;
-+ else if (strstr(product, "FX507ZC4"))
-+ max_boost = 15;
-+ else if (strstr(product, "GU605"))
-+ max_boost = 20;
-+
-+ /* ensure defaults for tunables */
-+ rog->cpu_default = cpu_default;
-+ rog->cpu_min = PPT_CPU_LIMIT_MIN;
-+ rog->cpu_max = cpu_max;
-+
-+ rog->platform_default = platform_default;
-+ rog->platform_max = PPT_PLATFORM_MIN;
-+ rog->platform_max = platform_max;
-+
-+ rog->ppt_pl1_spl = cpu_default;
-+ rog->ppt_pl2_sppt = cpu_default;
-+ rog->ppt_apu_sppt = cpu_default;
-+
-+ rog->ppt_platform_sppt = platform_default;
-+ rog->ppt_fppt = platform_default;
-+
-+ rog->nv_boost_default = NVIDIA_BOOST_MAX;
-+ rog->nv_boost_max = NVIDIA_BOOST_MIN;
-+ rog->nv_boost_max = max_boost;
-+ rog->nv_dynamic_boost = NVIDIA_BOOST_MIN;
-+
-+ rog->nv_temp_default = NVIDIA_TEMP_MAX;
-+ rog->nv_temp_max = NVIDIA_TEMP_MIN;
-+ rog->nv_temp_max = NVIDIA_TEMP_MAX;
-+ rog->nv_temp_target = NVIDIA_TEMP_MIN;
-+
-+ rog->dgpu_tgp_default = NVIDIA_POWER_DEFAULT;
-+ rog->dgpu_tgp_min = NVIDIA_POWER_MIN;
-+ rog->dgpu_tgp_max = NVIDIA_POWER_MAX;
-+ rog->dgpu_tgp = NVIDIA_POWER_MAX;
-+
-+}
-+
-+static int __init asus_fw_init(void)
-+{
-+ int err;
-+
-+ fw_attrs.pending_reboot = 0;
-+
-+ mutex_lock(&asus_armoury.mutex);
-+
-+ asus_armoury.rog_tunables = kzalloc(sizeof(struct rog_tunables), GFP_KERNEL);
-+ if (!asus_armoury.rog_tunables) {
-+ mutex_unlock(&asus_armoury.mutex);
-+ return -ENOMEM;
-+ }
-+ init_rog_tunables(asus_armoury.rog_tunables);
-+ init_max_cpu_cores();
-+
-+ err = asus_fw_attr_add();
-+ mutex_unlock(&asus_armoury.mutex);
-+ 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(fw_attr_class, MKDEV(0, 0));
-+ fw_attributes_class_put();
-+
-+ mutex_unlock(&asus_armoury.mutex);
-+}
-+
-+module_init(asus_fw_init);
-+module_exit(asus_fw_exit);
-+
-+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..25b2c055930e
---- /dev/null
-+++ b/drivers/platform/x86/asus-armoury.h
-@@ -0,0 +1,259 @@
-+/* SPDX-License-Identifier: GPL-2.0
-+ *
-+ * Definitions for kernel modules using asus-armoury driver
-+ *
-+ * Copyright (c) 2024 Luke Jones <luke@ljones.dev>
-+ */
-+
-+#ifndef _ASUS_BIOSCFG_H_
-+#define _ASUS_BIOSCFG_H_
-+
-+#include "firmware_attributes_class.h"
-+#include <linux/types.h>
-+
-+#define DRIVER_NAME "asus-armoury"
-+
-+static ssize_t attr_int_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 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 }, \
-+ .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_int_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)
-+
-+/* Requires current_value show&|store */
-+#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); \
-+__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_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)
-+
-+#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 \
-+}
-+
-+/* 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 \
-+}
-+
-+/*
-+ * ROG PPT attributes need a little different in setup as they
-+ * require rog_tunables members.
-+ */
-+
-+#define __ROG_TUNABLE_RW(_attr, _min, _max, _wmi) \
-+static ssize_t _attr##_current_value_store(struct kobject *kobj, \
-+ struct kobj_attribute *attr, \
-+ const char *buf, size_t count) \
-+{ \
-+ return attr_int_store(kobj, attr, buf, count, \
-+ asus_armoury.rog_tunables->_min, \
-+ asus_armoury.rog_tunables->_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 __ROG_TUNABLE_SHOW(_prop, _attrname, _val) \
-+static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
-+ struct kobj_attribute *attr, char *buf) \
-+{ \
-+ return sysfs_emit(buf, "%d\n", asus_armoury.rog_tunables->_val);\
-+} \
-+static struct kobj_attribute attr_##_attrname##_##_prop = \
-+ __ASUS_ATTR_RO(_attrname, _prop)
-+
-+#define ATTR_GROUP_ROG_TUNABLE(_attrname, _fsname, _wmi, _default, \
-+ _min, _max, _incstep, _dispname) \
-+__ROG_TUNABLE_SHOW(default_value, _attrname, _default); \
-+__ROG_TUNABLE_RW(_attrname, _min, _max, _wmi); \
-+__ROG_TUNABLE_SHOW(min_value, _attrname, _min); \
-+__ROG_TUNABLE_SHOW(max_value, _attrname, _max); \
-+__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", _incstep); \
-+__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 \
-+}
-+
-+#endif /* _ASUS_BIOSCFG_H_ */
-diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
-index bc9c5db38324..9faebd12bd49 100644
---- a/drivers/platform/x86/asus-wmi.c
-+++ b/drivers/platform/x86/asus-wmi.c
-@@ -97,6 +97,12 @@ module_param(fnlock_default, bool, 0444);
- #define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST 1
- #define ASUS_THROTTLE_THERMAL_POLICY_SILENT 2
-
-+#define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO 0
-+#define ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO 1
-+#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO 2
-+
-+#define PLATFORM_PROFILE_MAX 2
-+
- #define USB_INTEL_XUSB2PR 0xD0
- #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
-
-@@ -146,6 +152,20 @@ static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
-
- static int throttle_thermal_policy_write(struct asus_wmi *);
-
-+static const struct dmi_system_id asus_ally_mcu_quirk[] = {
-+ {
-+ .matches = {
-+ DMI_MATCH(DMI_BOARD_NAME, "RC71L"),
-+ },
-+ },
-+ {
-+ .matches = {
-+ DMI_MATCH(DMI_BOARD_NAME, "RC72L"),
-+ },
-+ },
-+ { },
-+};
-+
- static bool ashs_present(void)
- {
- int i = 0;
-@@ -285,8 +305,8 @@ struct asus_wmi {
- u32 kbd_rgb_dev;
- bool kbd_rgb_state_available;
-
-- bool throttle_thermal_policy_available;
- u8 throttle_thermal_policy_mode;
-+ u32 throttle_thermal_policy_dev;
-
- bool cpu_fan_curve_available;
- bool gpu_fan_curve_available;
-@@ -334,20 +354,29 @@ static int asus_wmi_evaluate_method3(u32 method_id,
- status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
- &input, &output);
-
-- if (ACPI_FAILURE(status))
-+ pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x, 0x%08x\n",
-+ __func__, method_id, arg0, arg1, arg2);
-+ if (ACPI_FAILURE(status)) {
-+ pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
-+ __func__, method_id, arg0, -EIO);
- return -EIO;
-+ }
-
- obj = (union acpi_object *)output.pointer;
- if (obj && obj->type == ACPI_TYPE_INTEGER)
- tmp = (u32) obj->integer.value;
-
-+ pr_debug("Result: 0x%08x\n", tmp);
- if (retval)
- *retval = tmp;
-
- kfree(obj);
-
-- if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
-+ if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) {
-+ pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
-+ __func__, method_id, arg0, -ENODEV);
- return -ENODEV;
-+ }
-
- return 0;
- }
-@@ -377,20 +406,29 @@ static int asus_wmi_evaluate_method5(u32 method_id,
- status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
- &input, &output);
-
-- if (ACPI_FAILURE(status))
-+ pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
-+ __func__, method_id, arg0, arg1, arg2, arg3, arg4);
-+ if (ACPI_FAILURE(status)) {
-+ pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
-+ __func__, method_id, arg0, -EIO);
- return -EIO;
-+ }
-
- obj = (union acpi_object *)output.pointer;
- if (obj && obj->type == ACPI_TYPE_INTEGER)
- tmp = (u32) obj->integer.value;
-
-+ pr_debug("Result: %x\n", tmp);
- if (retval)
- *retval = tmp;
-
- kfree(obj);
-
-- if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
-+ if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) {
-+ pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
-+ __func__, method_id, arg0, -ENODEV);
- return -ENODEV;
-+ }
-
- return 0;
- }
-@@ -416,8 +454,13 @@ static int asus_wmi_evaluate_method_buf(u32 method_id,
- status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
- &input, &output);
-
-- if (ACPI_FAILURE(status))
-+ pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x\n",
-+ __func__, method_id, arg0, arg1);
-+ if (ACPI_FAILURE(status)) {
-+ pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
-+ __func__, method_id, arg0, -EIO);
- return -EIO;
-+ }
-
- obj = (union acpi_object *)output.pointer;
-
-@@ -453,8 +496,11 @@ static int asus_wmi_evaluate_method_buf(u32 method_id,
-
- kfree(obj);
-
-- if (err)
-+ if (err) {
-+ pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
-+ __func__, method_id, arg0, err);
- return err;
-+ }
-
- return 0;
- }
-@@ -503,12 +549,56 @@ 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 function to call.
-+ * @retval: A pointer to where to store the value returned from WMI.
-+ *
-+ * The returned WMI function state can also be used to determine if the WMI
-+ * function is supported by checking if the asus_wmi_get_devstate_dsts()
-+ * returns an error.
-+ *
-+ * 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, or if it returns "unsupported" which is typically a 0
-+ * (no return, and no 'supported' bit set), or a 0xFFFFFFFE (~1) which if not
-+ * caught here can result in unexpected behaviour later.
-+ */
-+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;
-+ /* Be explicit about retval */
-+ if (*retval == 0xFFFFFFFE || *retval == 0)
-+ return -ENODEV;
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(asus_wmi_get_devstate_dsts);
-+
-+/**
-+ * 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_GPL(asus_wmi_set_devstate);
-
- /* Helper for special devices with magic return codes */
- static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
-@@ -542,6 +632,7 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
- {
- u32 retval;
- int status = asus_wmi_get_devstate(asus, dev_id, &retval);
-+ pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval);
-
- return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
- }
-@@ -1685,7 +1776,8 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
- goto error;
- }
-
-- if (!kbd_led_read(asus, &led_val, NULL)) {
-+ if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) {
-+ pr_info("using asus-wmi for asus::kbd_backlight\n");
- asus->kbd_led_wk = led_val;
- asus->kbd_led.name = "asus::kbd_backlight";
- asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
-@@ -3127,7 +3219,7 @@ static int fan_curve_get_factory_default(struct asus_wmi *asus, u32 fan_dev)
- int err, fan_idx;
- u8 mode = 0;
-
-- if (asus->throttle_thermal_policy_available)
-+ if (asus->throttle_thermal_policy_dev)
- mode = asus->throttle_thermal_policy_mode;
- /* DEVID_<C/G>PU_FAN_CURVE is switched for OVERBOOST vs SILENT */
- if (mode == 2)
-@@ -3334,7 +3426,7 @@ static ssize_t fan_curve_enable_store(struct device *dev,
- * For machines with throttle this is the only way to reset fans
- * to default mode of operation (does not erase curve data).
- */
-- if (asus->throttle_thermal_policy_available) {
-+ if (asus->throttle_thermal_policy_dev) {
- err = throttle_thermal_policy_write(asus);
- if (err)
- return err;
-@@ -3551,8 +3643,8 @@ static const struct attribute_group asus_fan_curve_attr_group = {
- __ATTRIBUTE_GROUPS(asus_fan_curve_attr);
-
- /*
-- * Must be initialised after throttle_thermal_policy_check_present() as
-- * we check the status of throttle_thermal_policy_available during init.
-+ * Must be initialised after throttle_thermal_policy_dev is set as
-+ * we check the status of throttle_thermal_policy_dev during init.
- */
- static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
- {
-@@ -3562,18 +3654,27 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
-
- err = fan_curve_check_present(asus, &asus->cpu_fan_curve_available,
- ASUS_WMI_DEVID_CPU_FAN_CURVE);
-- if (err)
-+ if (err) {
-+ pr_err("%s, checked 0x%08x, failed: %d\n",
-+ __func__, ASUS_WMI_DEVID_CPU_FAN_CURVE, err);
- return err;
-+ }
-
- err = fan_curve_check_present(asus, &asus->gpu_fan_curve_available,
- ASUS_WMI_DEVID_GPU_FAN_CURVE);
-- if (err)
-+ if (err) {
-+ pr_err("%s, checked 0x%08x, failed: %d\n",
-+ __func__, ASUS_WMI_DEVID_GPU_FAN_CURVE, err);
- return err;
-+ }
-
- err = fan_curve_check_present(asus, &asus->mid_fan_curve_available,
- ASUS_WMI_DEVID_MID_FAN_CURVE);
-- if (err)
-+ if (err) {
-+ pr_err("%s, checked 0x%08x, failed: %d\n",
-+ __func__, ASUS_WMI_DEVID_MID_FAN_CURVE, err);
- return err;
-+ }
-
- if (!asus->cpu_fan_curve_available
- && !asus->gpu_fan_curve_available
-@@ -3593,38 +3694,13 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
- }
-
- /* Throttle thermal policy ****************************************************/
--
--static int throttle_thermal_policy_check_present(struct asus_wmi *asus)
--{
-- u32 result;
-- int err;
--
-- asus->throttle_thermal_policy_available = false;
--
-- err = asus_wmi_get_devstate(asus,
-- ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
-- &result);
-- if (err) {
-- if (err == -ENODEV)
-- return 0;
-- return err;
-- }
--
-- if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
-- asus->throttle_thermal_policy_available = true;
--
-- return 0;
--}
--
- static int throttle_thermal_policy_write(struct asus_wmi *asus)
- {
-- int err;
-- u8 value;
-+ u8 value = asus->throttle_thermal_policy_mode;
- u32 retval;
-+ int err;
-
-- value = asus->throttle_thermal_policy_mode;
--
-- err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
-+ err = asus_wmi_set_devstate(asus->throttle_thermal_policy_dev,
- value, &retval);
-
- sysfs_notify(&asus->platform_device->dev.kobj, NULL,
-@@ -3654,7 +3730,7 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus)
-
- static int throttle_thermal_policy_set_default(struct asus_wmi *asus)
- {
-- if (!asus->throttle_thermal_policy_available)
-+ if (!asus->throttle_thermal_policy_dev)
- return 0;
-
- asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
-@@ -3666,7 +3742,7 @@ static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
- u8 new_mode = asus->throttle_thermal_policy_mode + 1;
- int err;
-
-- if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
-+ if (new_mode > PLATFORM_PROFILE_MAX)
- new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
-
- asus->throttle_thermal_policy_mode = new_mode;
-@@ -3705,7 +3781,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
- if (result < 0)
- return result;
-
-- if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
-+ if (new_mode > PLATFORM_PROFILE_MAX)
- return -EINVAL;
-
- asus->throttle_thermal_policy_mode = new_mode;
-@@ -3722,10 +3798,52 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
- return count;
- }
-
--// Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
-+/*
-+ * Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
-+ */
- static DEVICE_ATTR_RW(throttle_thermal_policy);
-
- /* Platform profile ***********************************************************/
-+static int asus_wmi_platform_profile_to_vivo(struct asus_wmi *asus, int mode)
-+{
-+ bool vivo;
-+
-+ vivo = asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO;
-+
-+ if (vivo) {
-+ switch (mode) {
-+ case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
-+ return ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO;
-+ case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST:
-+ return ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO;
-+ case ASUS_THROTTLE_THERMAL_POLICY_SILENT:
-+ return ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO;
-+ }
-+ }
-+
-+ return mode;
-+}
-+
-+static int asus_wmi_platform_profile_mode_from_vivo(struct asus_wmi *asus, int mode)
-+{
-+ bool vivo;
-+
-+ vivo = asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO;
-+
-+ if (vivo) {
-+ switch (mode) {
-+ case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO:
-+ return ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
-+ case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO:
-+ return ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST;
-+ case ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO:
-+ return ASUS_THROTTLE_THERMAL_POLICY_SILENT;
-+ }
-+ }
-+
-+ return mode;
-+}
-+
- static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
- enum platform_profile_option *profile)
- {
-@@ -3733,10 +3851,9 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
- int tp;
-
- asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
--
- tp = asus->throttle_thermal_policy_mode;
-
-- switch (tp) {
-+ switch (asus_wmi_platform_profile_mode_from_vivo(asus, tp)) {
- case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
- *profile = PLATFORM_PROFILE_BALANCED;
- break;
-@@ -3775,7 +3892,7 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
- return -EOPNOTSUPP;
- }
-
-- asus->throttle_thermal_policy_mode = tp;
-+ asus->throttle_thermal_policy_mode = asus_wmi_platform_profile_to_vivo(asus, tp);
- return throttle_thermal_policy_write(asus);
- }
-
-@@ -3788,7 +3905,7 @@ static int platform_profile_setup(struct asus_wmi *asus)
- * Not an error if a component platform_profile relies on is unavailable
- * so early return, skipping the setup of platform_profile.
- */
-- if (!asus->throttle_thermal_policy_available)
-+ if (!asus->throttle_thermal_policy_dev)
- return 0;
-
- dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n");
-@@ -3803,8 +3920,13 @@ static int platform_profile_setup(struct asus_wmi *asus)
- asus->platform_profile_handler.choices);
-
- err = platform_profile_register(&asus->platform_profile_handler);
-- if (err)
-+ if (err == -EEXIST) {
-+ pr_warn("%s, a platform_profile handler is already registered\n", __func__);
-+ return 0;
-+ } else if (err) {
-+ pr_err("%s, failed at platform_profile_register: %d\n", __func__, err);
- return err;
-+ }
-
- asus->platform_profile_support = true;
- return 0;
-@@ -4203,7 +4325,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
- if (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) {
- if (asus->fan_boost_mode_available)
- fan_boost_mode_switch_next(asus);
-- if (asus->throttle_thermal_policy_available)
-+ if (asus->throttle_thermal_policy_dev)
- throttle_thermal_policy_switch_next(asus);
- return;
-
-@@ -4375,7 +4497,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
- else if (attr == &dev_attr_fan_boost_mode.attr)
- ok = asus->fan_boost_mode_available;
- else if (attr == &dev_attr_throttle_thermal_policy.attr)
-- ok = asus->throttle_thermal_policy_available;
-+ ok = asus->throttle_thermal_policy_dev != 0;
- else if (attr == &dev_attr_ppt_pl2_sppt.attr)
- devid = ASUS_WMI_DEVID_PPT_PL2_SPPT;
- else if (attr == &dev_attr_ppt_pl1_spl.attr)
-@@ -4401,8 +4523,10 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
- else if (attr == &dev_attr_available_mini_led_mode.attr)
- ok = asus->mini_led_dev_id != 0;
-
-- if (devid != -1)
-+ if (devid != -1) {
- ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
-+ pr_debug("%s called 0x%08x, ok: %x\n", __func__, devid, ok);
-+ }
-
- return ok ? attr->mode : 0;
- }
-@@ -4650,7 +4774,7 @@ static int asus_wmi_add(struct platform_device *pdev)
- 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_match(DMI_BOARD_NAME, "RC71L");
-+ && 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;
-@@ -4667,18 +4791,17 @@ static int asus_wmi_add(struct platform_device *pdev)
- else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2))
- asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2;
-
-+ 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;
-+
- err = fan_boost_mode_check_present(asus);
- if (err)
- goto fail_fan_boost_mode;
-
-- err = throttle_thermal_policy_check_present(asus);
-- if (err)
-- goto fail_throttle_thermal_policy;
-- else
-- throttle_thermal_policy_set_default(asus);
--
- err = platform_profile_setup(asus);
-- if (err)
-+ if (err && err != -EEXIST)
- goto fail_platform_profile_setup;
-
- err = asus_wmi_sysfs_init(asus->platform_device);
-@@ -4771,7 +4894,6 @@ static int asus_wmi_add(struct platform_device *pdev)
- fail_input:
- asus_wmi_sysfs_exit(asus->platform_device);
- fail_sysfs:
--fail_throttle_thermal_policy:
- fail_custom_fan_curve:
- fail_platform_profile_setup:
- if (asus->platform_profile_support)
-diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile
-index 9f16cb514397..a8aba07bf1dc 100644
---- a/drivers/platform/x86/intel/int3472/Makefile
-+++ b/drivers/platform/x86/intel/int3472/Makefile
-@@ -1,4 +1,7 @@
- obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \
-- intel_skl_int3472_tps68470.o
--intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o common.o
--intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o
-+ intel_skl_int3472_tps68470.o \
-+ intel_skl_int3472_common.o
-+intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o
-+intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o
-+
-+intel_skl_int3472_common-y += common.o
-diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c
-index 9db2bb0bbba4..b3a2578e06c1 100644
---- a/drivers/platform/x86/intel/int3472/common.c
-+++ b/drivers/platform/x86/intel/int3472/common.c
-@@ -29,6 +29,7 @@ union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *i
-
- return obj;
- }
-+EXPORT_SYMBOL_GPL(skl_int3472_get_acpi_buffer);
-
- int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb)
- {
-@@ -52,6 +53,7 @@ int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb)
- kfree(obj);
- return ret;
- }
-+EXPORT_SYMBOL_GPL(skl_int3472_fill_cldb);
-
- /* sensor_adev_ret may be NULL, name_ret must not be NULL */
- int skl_int3472_get_sensor_adev_and_name(struct device *dev,
-@@ -80,3 +82,8 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev,
-
- return ret;
- }
-+EXPORT_SYMBOL_GPL(skl_int3472_get_sensor_adev_and_name);
-+
-+MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver library");
-+MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
-+MODULE_LICENSE("GPL");
-diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
-index 3eb5cd6773ad..a23cc8624218 100644
---- a/include/linux/platform_data/x86/asus-wmi.h
-+++ b/include/linux/platform_data/x86/asus-wmi.h
-@@ -4,6 +4,7 @@
-
- #include <linux/errno.h>
- #include <linux/types.h>
-+#include <linux/dmi.h>
-
- /* WMI Methods */
- #define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
-@@ -64,8 +65,10 @@
- #define ASUS_WMI_DEVID_SCREENPAD_LIGHT 0x00050032
- #define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018
- #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
-+#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
-@@ -126,6 +129,14 @@
- /* 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_DGPU_BASE_TGP 0x00120099
-+#define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
-+#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
-@@ -151,8 +162,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 int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
-+{
-+ return -ENODEV;
-+}
-+static 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)
- {
-@@ -160,4 +181,39 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
- }
- #endif
-
-+/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
-+static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = {
-+ {
-+ .matches = {
-+ DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"),
-+ },
-+ },
-+ {
-+ .matches = {
-+ DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"),
-+ },
-+ },
-+ {
-+ .matches = {
-+ DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"),
-+ },
-+ },
-+ {
-+ .matches = {
-+ DMI_MATCH(DMI_BOARD_NAME, "GA403U"),
-+ },
-+ },
-+ {
-+ .matches = {
-+ DMI_MATCH(DMI_BOARD_NAME, "GU605M"),
-+ },
-+ },
-+ {
-+ .matches = {
-+ DMI_MATCH(DMI_BOARD_NAME, "RC71L"),
-+ },
-+ },
-+ { },
-+};
-+
- #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
-diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
-index 3840565ef8b0..5641b40b9cdc 100644
---- a/sound/pci/hda/patch_realtek.c
-+++ b/sound/pci/hda/patch_realtek.c
-@@ -7409,6 +7409,7 @@ enum {
- ALC285_FIXUP_THINKPAD_X1_GEN7,
- ALC285_FIXUP_THINKPAD_HEADSET_JACK,
- ALC294_FIXUP_ASUS_ALLY,
-+ ALC294_FIXUP_ASUS_ALLY_X,
- ALC294_FIXUP_ASUS_ALLY_PINS,
- ALC294_FIXUP_ASUS_ALLY_VERBS,
- ALC294_FIXUP_ASUS_ALLY_SPEAKER,
-@@ -8875,6 +8876,12 @@ static const struct hda_fixup alc269_fixups[] = {
- .chained = true,
- .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS
- },
-+ [ALC294_FIXUP_ASUS_ALLY_X] = {
-+ .type = HDA_FIXUP_FUNC,
-+ .v.func = tas2781_fixup_i2c,
-+ .chained = true,
-+ .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS
-+ },
- [ALC294_FIXUP_ASUS_ALLY_PINS] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
-@@ -10307,6 +10314,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS),
- SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK),
- SND_PCI_QUIRK(0x1043, 0x17f3, "ROG Ally NR2301L/X", ALC294_FIXUP_ASUS_ALLY),
-+ SND_PCI_QUIRK(0x1043, 0x1eb3, "ROG Ally X RC72LA", ALC294_FIXUP_ASUS_ALLY_X),
- SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2),
- SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS),
- SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),