aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SOURCES/0001-NOT-FOR-UPSTREAM-PM-suspend-Disable-s2idle-on-Steam-.patch81
-rw-r--r--SOURCES/0001-Remove-REBAR-size-quirk-for-Sapphire-RX-5600-XT-Puls.patch28
-rw-r--r--SOURCES/0001-Revert-PCI-Add-a-REBAR-size-quirk-for-Sapphire-RX-56.patch34
-rw-r--r--SOURCES/0001-add-acpi_call.patch506
-rw-r--r--SOURCES/0001-amd-hdr.patch58
-rw-r--r--SOURCES/0001-hid-asus-nero-patches-rogue.patch940
-rw-r--r--SOURCES/ROG-ALLY-NCT6775-PLATFORM.patch12
-rw-r--r--SOURCES/acso.patch195
-rw-r--r--SOURCES/amdgpu-bug-fix.patch617
-rw-r--r--SOURCES/kernel-aarch64-16k-debug-fedora.config3
-rw-r--r--SOURCES/kernel-aarch64-16k-fedora.config3
-rw-r--r--SOURCES/kernel-aarch64-64k-debug-rhel.config3
-rw-r--r--SOURCES/kernel-aarch64-64k-rhel.config3
-rw-r--r--SOURCES/kernel-aarch64-debug-fedora.config3
-rw-r--r--SOURCES/kernel-aarch64-debug-rhel.config3
-rw-r--r--SOURCES/kernel-aarch64-fedora.config3
-rw-r--r--SOURCES/kernel-aarch64-rhel.config3
-rw-r--r--SOURCES/kernel-aarch64-rt-debug-rhel.config3
-rw-r--r--SOURCES/kernel-aarch64-rt-rhel.config3
-rw-r--r--SOURCES/kernel-ppc64le-debug-fedora.config3
-rw-r--r--SOURCES/kernel-ppc64le-debug-rhel.config3
-rw-r--r--SOURCES/kernel-ppc64le-fedora.config3
-rw-r--r--SOURCES/kernel-ppc64le-rhel.config3
-rw-r--r--SOURCES/kernel-s390x-debug-fedora.config3
-rw-r--r--SOURCES/kernel-s390x-debug-rhel.config3
-rw-r--r--SOURCES/kernel-s390x-fedora.config3
-rw-r--r--SOURCES/kernel-s390x-rhel.config3
-rw-r--r--SOURCES/kernel-s390x-zfcpdump-rhel.config3
-rw-r--r--SOURCES/kernel-x86_64-debug-fedora.config20
-rw-r--r--SOURCES/kernel-x86_64-debug-rhel.config20
-rw-r--r--SOURCES/kernel-x86_64-fedora.config20
-rw-r--r--SOURCES/kernel-x86_64-rhel.config20
-rw-r--r--SOURCES/kernel-x86_64-rt-debug-rhel.config20
-rw-r--r--SOURCES/kernel-x86_64-rt-rhel.config20
-rw-r--r--SOURCES/linux-surface.patch12
-rw-r--r--SOURCES/patch-6.6-redhat.patch6
-rw-r--r--SOURCES/rog-ally-alsa.patch374
-rw-r--r--SOURCES/rog-ally-audio-fix.patch64
-rw-r--r--SOURCES/rog-ally-bmc150.patch2672
-rw-r--r--SOURCES/steam-deck.patch224
-rw-r--r--SOURCES/steamdeck-oled-audio.patch437
-rw-r--r--SOURCES/steamdeck-oled-bt.patch239
-rw-r--r--SOURCES/steamdeck-oled-hw-quirks.patch395
-rw-r--r--SOURCES/steamdeck-oled-wifi.patch3753
-rw-r--r--SOURCES/uinput.patch133
-rw-r--r--SOURCES/v10-0001-HID-asus-fix-more-n-key-report-descriptors-if-n-.patch89
-rw-r--r--SOURCES/v10-0002-HID-asus-make-asus_kbd_init-generic-remove-rog_n.patch130
-rw-r--r--SOURCES/v10-0003-HID-asus-add-ROG-Ally-N-Key-ID-and-keycodes.patch (renamed from SOURCES/rog-ally-side-keys-fix.patch)41
-rw-r--r--SOURCES/v10-0004-HID-asus-add-ROG-Ally-xpad-settings.patch1471
-rw-r--r--SOURCES/v2-0001-platform-x86-asus-wmi-disable-USB0-hub-on-ROG-All.patch113
-rw-r--r--SPECS/kernel.spec91
-rwxr-xr-xTOOLS/patch_configs.py23
52 files changed, 12060 insertions, 855 deletions
diff --git a/SOURCES/0001-NOT-FOR-UPSTREAM-PM-suspend-Disable-s2idle-on-Steam-.patch b/SOURCES/0001-NOT-FOR-UPSTREAM-PM-suspend-Disable-s2idle-on-Steam-.patch
new file mode 100644
index 0000000..349a6a6
--- /dev/null
+++ b/SOURCES/0001-NOT-FOR-UPSTREAM-PM-suspend-Disable-s2idle-on-Steam-.patch
@@ -0,0 +1,81 @@
+From d6935e6d62b35ec14d2187c2cfdc19934d66db6d Mon Sep 17 00:00:00 2001
+From: "Guilherme G. Piccoli" <gpiccoli@igalia.com>
+Date: Thu, 2 Nov 2023 16:28:35 -0300
+Subject: [PATCH] (NOT-FOR-UPSTREAM) PM: suspend: Disable s2idle on Steam Deck
+
+Jupiter platform doesn't support s2idle - system appears to
+sleep and never wakeup again. So, disable it based on DMI
+checking for platforms named "Valve Jupiter".
+
+Signed-off-by: Guilherme G. Piccoli <gpiccoli@igalia.com>
+(cherry picked from commit 3956de585667cfd9f91a3ee8af34f6c0d44f4381)
+Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
+Signed-off-by: Jan200101 <sentrycraft123@gmail.com>
+---
+ kernel/power/suspend.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
+index fa3bf161d13f..e614b865a099 100644
+--- a/kernel/power/suspend.c
++++ b/kernel/power/suspend.c
+@@ -30,6 +30,7 @@
+ #include <trace/events/power.h>
+ #include <linux/compiler.h>
+ #include <linux/moduleparam.h>
++#include <linux/dmi.h>
+
+ #include "power.h"
+
+@@ -60,6 +61,7 @@ static DECLARE_SWAIT_QUEUE_HEAD(s2idle_wait_head);
+
+ enum s2idle_states __read_mostly s2idle_state;
+ static DEFINE_RAW_SPINLOCK(s2idle_lock);
++static bool s2idle_unsupported;
+
+ /**
+ * pm_suspend_default_s2idle - Check if suspend-to-idle is the default suspend.
+@@ -174,6 +176,8 @@ static bool valid_state(suspend_state_t state)
+
+ void __init pm_states_init(void)
+ {
++ const char *sys_vendor, *product_name;
++
+ /* "mem" and "freeze" are always present in /sys/power/state. */
+ pm_states[PM_SUSPEND_MEM] = pm_labels[PM_SUSPEND_MEM];
+ pm_states[PM_SUSPEND_TO_IDLE] = pm_labels[PM_SUSPEND_TO_IDLE];
+@@ -182,6 +186,20 @@ void __init pm_states_init(void)
+ * initialize mem_sleep_states[] accordingly here.
+ */
+ mem_sleep_states[PM_SUSPEND_TO_IDLE] = mem_sleep_labels[PM_SUSPEND_TO_IDLE];
++
++ /*
++ * Identify here if we're running on Steam Deck - if so, we shouldn't
++ * support s2idle mem_sleep mode, since this mode doesn't work on Deck.
++ */
++
++ sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
++ product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
++
++ if (sys_vendor && (!strncmp("Valve", sys_vendor, 5)) &&
++ product_name && (!strncmp("Jupiter", product_name, 7))) {
++ s2idle_unsupported = true;
++ pr_info("Steam Deck quirk - no s2idle allowed!\n");
++ }
+ }
+
+ static int __init mem_sleep_default_setup(char *str)
+@@ -562,6 +580,10 @@ static int enter_state(suspend_state_t state)
+ return -EAGAIN;
+ }
+ #endif
++ if (s2idle_unsupported) {
++ pr_info("s2idle sleep is not supported\n");
++ return -EINVAL;
++ }
+ } else if (!valid_state(state)) {
+ return -EINVAL;
+ }
+--
+2.43.0
+
diff --git a/SOURCES/0001-Remove-REBAR-size-quirk-for-Sapphire-RX-5600-XT-Puls.patch b/SOURCES/0001-Remove-REBAR-size-quirk-for-Sapphire-RX-5600-XT-Puls.patch
new file mode 100644
index 0000000..0174537
--- /dev/null
+++ b/SOURCES/0001-Remove-REBAR-size-quirk-for-Sapphire-RX-5600-XT-Puls.patch
@@ -0,0 +1,28 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jan200101 <sentrycraft123@gmail.com>
+Date: Fri, 1 Dec 2023 20:31:09 +0100
+Subject: [PATCH] Remove REBAR size quirk for Sapphire RX 5600 XT Pulse
+
+This partially reverts commit 907830b0fc9e374d00f3c83de5e426157b482c01.
+
+Signed-off-by: Jan200101 <sentrycraft123@gmail.com>
+---
+ drivers/pci/pci.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
+index 55bc3576a985..aca636182525 100644
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -3755,11 +3755,6 @@ u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
+ cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
+
+- /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
+- if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
+- bar == 0 && cap == 0x700)
+- return 0x3f00;
+-
+ return cap;
+ }
+ EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
diff --git a/SOURCES/0001-Revert-PCI-Add-a-REBAR-size-quirk-for-Sapphire-RX-56.patch b/SOURCES/0001-Revert-PCI-Add-a-REBAR-size-quirk-for-Sapphire-RX-56.patch
deleted file mode 100644
index e68df11..0000000
--- a/SOURCES/0001-Revert-PCI-Add-a-REBAR-size-quirk-for-Sapphire-RX-56.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 4b4ce124699c160925e5fdeb147a78f79d38351f Mon Sep 17 00:00:00 2001
-From: Simon May <simon.may@protonmail.ch>
-Date: Sun, 19 Sep 2021 23:45:59 +0200
-Subject: [PATCH] Revert "PCI: Add a REBAR size quirk for Sapphire RX 5600 XT
- Pulse"
-
-This reverts commit 907830b0fc9e374d00f3c83de5e426157b482c01.
----
- drivers/pci/pci.c | 9 +--------
- 1 file changed, 1 insertion(+), 8 deletions(-)
-
-diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
-index 3f353572588d..1c8cc4b98f95 100644
---- a/drivers/pci/pci.c
-+++ b/drivers/pci/pci.c
-@@ -3637,14 +3637,7 @@ u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
- return 0;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
-- cap &= PCI_REBAR_CAP_SIZES;
--
-- /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
-- if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
-- bar == 0 && cap == 0x7000)
-- cap = 0x3f000;
--
-- return cap >> 4;
-+ return (cap & PCI_REBAR_CAP_SIZES) >> 4;
- }
- EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
-
---
-2.30.2
-
diff --git a/SOURCES/0001-add-acpi_call.patch b/SOURCES/0001-add-acpi_call.patch
new file mode 100644
index 0000000..b0a185a
--- /dev/null
+++ b/SOURCES/0001-add-acpi_call.patch
@@ -0,0 +1,506 @@
+From 3f14226e2e90dba5d72c106da29e1876eb7b88ff Mon Sep 17 00:00:00 2001
+From: Denis <benato.denis96@gmail.com>
+Date: Thu, 28 Sep 2023 03:40:53 +0200
+Subject: [PATCH] add acpi_call
+
+---
+ drivers/platform/x86/Kconfig | 5 +
+ drivers/platform/x86/Makefile | 4 +
+ drivers/platform/x86/acpi_call.c | 449 +++++++++++++++++++++++++++++++
+ 3 files changed, 458 insertions(+)
+ create mode 100644 drivers/platform/x86/acpi_call.c
+
+diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
+index 49c2c4cd8d00..fde791e51261 100644
+--- a/drivers/platform/x86/Kconfig
++++ b/drivers/platform/x86/Kconfig
+@@ -170,6 +170,11 @@ config ACER_WIRELESS
+ If you choose to compile this driver as a module the module will be
+ called acer-wireless.
+
++config ACPI_CALL
++ tristate "acpi_call module"
++ help
++ This embeds acpi_call module into the kernel
++
+ config ACER_WMI
+ tristate "Acer WMI Laptop Extras"
+ depends on BACKLIGHT_CLASS_DEVICE
+diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
+index 52dfdf574ac2..1e434fcb8273 100644
+--- a/drivers/platform/x86/Makefile
++++ b/drivers/platform/x86/Makefile
+@@ -4,10 +4,14 @@
+ # x86 Platform-Specific Drivers
+ #
+
++# ACPI calls
++
+ # Windows Management Interface
+ obj-$(CONFIG_ACPI_WMI) += wmi.o
+ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
+
++obj-$(CONFIG_ACPI_CALL) += acpi_call.o
++
+ # WMI drivers
+ obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
+ obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
+diff --git a/drivers/platform/x86/acpi_call.c b/drivers/platform/x86/acpi_call.c
+new file mode 100644
+index 000000000000..d7bc238e16da
+--- /dev/null
++++ b/drivers/platform/x86/acpi_call.c
+@@ -0,0 +1,449 @@
++/* Copyright (c) 2010: Michal Kottman */
++
++#define BUILDING_ACPICA
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++#include <linux/proc_fs.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
++#include <asm/uaccess.h>
++#endif
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
++#include <linux/acpi.h>
++#else
++#include <acpi/acpi.h>
++#endif
++
++MODULE_LICENSE("GPL");
++
++/* Uncomment the following line to enable debug messages */
++/*
++#define DEBUG
++*/
++
++#define BUFFER_SIZE 4096
++#define INPUT_BUFFER_SIZE (2 * BUFFER_SIZE)
++#define MAX_ACPI_ARGS 16
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
++#define HAVE_PROC_CREATE
++#endif
++
++extern struct proc_dir_entry *acpi_root_dir;
++
++static char input_buffer[INPUT_BUFFER_SIZE];
++static char result_buffer[BUFFER_SIZE];
++static char not_called_message[11] = "not called";
++
++static u8 temporary_buffer[BUFFER_SIZE];
++
++static size_t get_avail_bytes(void) {
++ return BUFFER_SIZE - strlen(result_buffer);
++}
++static char *get_buffer_end(void) {
++ return result_buffer + strlen(result_buffer);
++}
++
++/** Appends the contents of an acpi_object to the result buffer
++@param result An acpi object holding result data
++@returns 0 if the result could fully be saved, a higher value otherwise
++*/
++static int acpi_result_to_string(union acpi_object *result) {
++ if (result->type == ACPI_TYPE_INTEGER) {
++ snprintf(get_buffer_end(), get_avail_bytes(),
++ "0x%x", (int)result->integer.value);
++ } else if (result->type == ACPI_TYPE_STRING) {
++ snprintf(get_buffer_end(), get_avail_bytes(),
++ "\"%*s\"", result->string.length, result->string.pointer);
++ } else if (result->type == ACPI_TYPE_BUFFER) {
++ int i;
++ // do not store more than data if it does not fit. The first element is
++ // just 4 chars, but there is also two bytes from the curly brackets
++ int show_values = min((size_t)result->buffer.length, get_avail_bytes() / 6);
++
++ snprintf(get_buffer_end(), get_avail_bytes(), "{");
++ for (i = 0; i < show_values; i++)
++ sprintf(get_buffer_end(),
++ i == 0 ? "0x%02x" : ", 0x%02x", result->buffer.pointer[i]);
++
++ if (result->buffer.length > show_values) {
++ // if data was truncated, show a trailing comma if there is space
++ snprintf(get_buffer_end(), get_avail_bytes(), ",");
++ return 1;
++ } else {
++ // in case show_values == 0, but the buffer is too small to hold
++ // more values (i.e. the buffer cannot have anything more than "{")
++ snprintf(get_buffer_end(), get_avail_bytes(), "}");
++ }
++ } else if (result->type == ACPI_TYPE_PACKAGE) {
++ int i;
++ snprintf(get_buffer_end(), get_avail_bytes(), "[");
++ for (i=0; i<result->package.count; i++) {
++ if (i > 0)
++ snprintf(get_buffer_end(), get_avail_bytes(), ", ");
++
++ // abort if there is no more space available
++ if (!get_avail_bytes() || acpi_result_to_string(&result->package.elements[i]))
++ return 1;
++ }
++ snprintf(get_buffer_end(), get_avail_bytes(), "]");
++ } else {
++ snprintf(get_buffer_end(), get_avail_bytes(),
++ "Object type 0x%x\n", result->type);
++ }
++
++ // return 0 if there are still bytes available, 1 otherwise
++ return !get_avail_bytes();
++}
++
++/**
++@param method The full name of ACPI method to call
++@param argc The number of parameters
++@param argv A pre-allocated array of arguments of type acpi_object
++*/
++static void do_acpi_call(const char * method, int argc, union acpi_object *argv)
++{
++ acpi_status status;
++ acpi_handle handle;
++ struct acpi_object_list arg;
++ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
++
++#ifdef DEBUG
++ printk(KERN_INFO "acpi_call: Calling %s\n", method);
++#endif
++
++ // get the handle of the method, must be a fully qualified path
++ status = acpi_get_handle(NULL, (acpi_string) method, &handle);
++
++ if (ACPI_FAILURE(status))
++ {
++ snprintf(result_buffer, BUFFER_SIZE, "Error: %s", acpi_format_exception(status));
++ printk(KERN_ERR "acpi_call: Cannot get handle: %s\n", result_buffer);
++ return;
++ }
++
++ // prepare parameters
++ arg.count = argc;
++ arg.pointer = argv;
++
++ // call the method
++ status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
++ if (ACPI_FAILURE(status))
++ {
++ snprintf(result_buffer, BUFFER_SIZE, "Error: %s", acpi_format_exception(status));
++ printk(KERN_ERR "acpi_call: Method call failed: %s\n", result_buffer);
++ return;
++ }
++
++ // reset the result buffer
++ *result_buffer = '\0';
++ acpi_result_to_string(buffer.pointer);
++ kfree(buffer.pointer);
++
++#ifdef DEBUG
++ printk(KERN_INFO "acpi_call: Call successful: %s\n", result_buffer);
++#endif
++}
++
++/** Decodes 2 hex characters to an u8 int
++*/
++u8 decodeHex(char *hex) {
++ char buf[3] = { hex[0], hex[1], 0};
++ return (u8) simple_strtoul(buf, NULL, 16);
++}
++
++/** Parses method name and arguments
++@param input Input string to be parsed. Modified in the process.
++@param nargs Set to number of arguments parsed (output)
++@param args
++*/
++static char *parse_acpi_args(char *input, int *nargs, union acpi_object **args)
++{
++ char *s = input;
++ int i;
++
++ *nargs = 0;
++ *args = NULL;
++
++ // the method name is separated from the arguments by a space
++ while (*s && *s != ' ')
++ s++;
++ // if no space is found, return 0 arguments
++ if (*s == 0)
++ return input;
++
++ *args = (union acpi_object *) kmalloc(MAX_ACPI_ARGS * sizeof(union acpi_object), GFP_KERNEL);
++ if (!*args) {
++ printk(KERN_ERR "acpi_call: unable to allocate buffer\n");
++ return NULL;
++ }
++
++ while (*s) {
++ if (*s == ' ') {
++ if (*nargs == 0)
++ *s = 0; // change first space to nul
++ ++ *nargs;
++ ++ s;
++ } else {
++ union acpi_object *arg = (*args) + (*nargs - 1);
++ if (*s == '"') {
++ // decode string
++ arg->type = ACPI_TYPE_STRING;
++ arg->string.pointer = ++s;
++ arg->string.length = 0;
++ while (*s && *s++ != '"')
++ arg->string.length ++;
++ // skip the last "
++ if (*s == '"')
++ ++s;
++ } else if (*s == 'b') {
++ // decode buffer - bXXXX
++ char *p = ++s;
++ int len = 0, i;
++ u8 *buf = NULL;
++
++ while (*p && *p!=' ')
++ p++;
++
++ len = p - s;
++ if (len % 2 == 1) {
++ printk(KERN_ERR "acpi_call: buffer arg%d is not multiple of 8 bits\n", *nargs);
++ --*nargs;
++ goto err;
++ }
++ len /= 2;
++
++ buf = (u8*) kmalloc(len, GFP_KERNEL);
++ if (!buf) {
++ printk(KERN_ERR "acpi_call: unable to allocate buffer\n");
++ --*nargs;
++ goto err;
++ }
++ for (i=0; i<len; i++) {
++ buf[i] = decodeHex(s + i*2);
++ }
++ s = p;
++
++ arg->type = ACPI_TYPE_BUFFER;
++ arg->buffer.pointer = buf;
++ arg->buffer.length = len;
++ } else if (*s == '{') {
++ // decode buffer - { b1, b2 ...}
++ u8 *buf = temporary_buffer;
++ arg->type = ACPI_TYPE_BUFFER;
++ arg->buffer.pointer = buf;
++ arg->buffer.length = 0;
++ while (*s && *s++ != '}') {
++ if (buf >= temporary_buffer + sizeof(temporary_buffer)) {
++ printk(KERN_ERR "acpi_call: buffer arg%d is truncated because the buffer is full\n", *nargs);
++ // clear remaining arguments
++ while (*s && *s != '}')
++ ++s;
++ break;
++ }
++ else if (*s >= '0' && *s <= '9') {
++ // decode integer into buffer
++ arg->buffer.length ++;
++ if (s[0] == '0' && s[1] == 'x')
++ *buf++ = simple_strtol(s+2, 0, 16);
++ else
++ *buf++ = simple_strtol(s, 0, 10);
++ }
++ // skip until space or comma or '}'
++ while (*s && *s != ' ' && *s != ',' && *s != '}')
++ ++s;
++ }
++ // store the result in new allocated buffer
++ buf = (u8*) kmalloc(arg->buffer.length, GFP_KERNEL);
++ if (!buf) {
++ printk(KERN_ERR "acpi_call: unable to allocate buffer\n");
++ --*nargs;
++ goto err;
++ }
++ memcpy(buf, temporary_buffer, arg->buffer.length);
++ arg->buffer.pointer = buf;
++ } else {
++ // decode integer, N or 0xN
++ arg->type = ACPI_TYPE_INTEGER;
++ if (s[0] == '0' && s[1] == 'x') {
++ arg->integer.value = simple_strtol(s+2, 0, 16);
++ } else {
++ arg->integer.value = simple_strtol(s, 0, 10);
++ }
++ while (*s && *s != ' ') {
++ ++s;
++ }
++ }
++ }
++ }
++
++ return input;
++
++err:
++ for (i=0; i<*nargs; i++)
++ if ((*args)[i].type == ACPI_TYPE_BUFFER && (*args)[i].buffer.pointer)
++ kfree((*args)[i].buffer.pointer);
++ kfree(*args);
++ return NULL;
++}
++
++/** procfs write callback. Called when writing into /proc/acpi/call.
++*/
++#ifdef HAVE_PROC_CREATE
++static ssize_t acpi_proc_write( struct file *filp, const char __user *buff,
++ size_t len, loff_t *data )
++#else
++static int acpi_proc_write( struct file *filp, const char __user *buff,
++ unsigned long len, void *data )
++#endif
++{
++ union acpi_object *args;
++ int nargs, i;
++ char *method;
++
++ memset(input_buffer, 0, INPUT_BUFFER_SIZE);
++ if (len > sizeof(input_buffer) - 1) {
++#ifdef HAVE_PROC_CREATE
++ printk(KERN_ERR "acpi_call: Input too long! (%zu)\n", len);
++#else
++ printk(KERN_ERR "acpi_call: Input too long! (%lu)\n", len);
++#endif
++ return -ENOSPC;
++ }
++
++ if (copy_from_user( input_buffer, buff, len )) {
++ return -EFAULT;
++ }
++ input_buffer[len] = '\0';
++ if (input_buffer[len-1] == '\n')
++ input_buffer[len-1] = '\0';
++
++ method = parse_acpi_args(input_buffer, &nargs, &args);
++ if (method) {
++ do_acpi_call(method, nargs, args);
++ if (args) {
++ for (i=0; i<nargs; i++)
++ if (args[i].type == ACPI_TYPE_BUFFER)
++ kfree(args[i].buffer.pointer);
++ }
++ }
++ if (args)
++ kfree(args);
++
++ return len;
++}
++
++/** procfs 'call' read callback. Called when reading the content of /proc/acpi/call.
++Returns the last call status:
++- "not called" when no call was previously issued
++- "failed" if the call failed
++- "ok" if the call succeeded
++*/
++#ifdef HAVE_PROC_CREATE
++static ssize_t acpi_proc_read( struct file *filp, char __user *buff,
++ size_t count, loff_t *off )
++{
++ ssize_t ret;
++ int len = strlen(result_buffer);
++
++ if(len == 0) {
++ ret = simple_read_from_buffer(buff, count, off, not_called_message, strlen(not_called_message) + 1);
++ } else if(len + 1 > count) {
++ // user buffer is too small
++ ret = 0;
++ } else if(*off == len + 1) {
++ // we're done
++ ret = 0;
++ result_buffer[0] = '\0';
++ } else {
++ // output the current result buffer
++ ret = simple_read_from_buffer(buff, count, off, result_buffer, len + 1);
++ *off = ret;
++ }
++
++ return ret;
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
++static struct proc_ops proc_acpi_operations = {
++ .proc_read = acpi_proc_read,
++ .proc_write = acpi_proc_write,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
++ .proc_lseek = default_llseek,
++#endif
++};
++#else
++static struct file_operations proc_acpi_operations = {
++ .owner = THIS_MODULE,
++ .read = acpi_proc_read,
++ .write = acpi_proc_write,
++};
++#endif
++
++#else
++static int acpi_proc_read(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int len = 0;
++
++ if (off > 0) {
++ *eof = 1;
++ return 0;
++ }
++
++ // output the current result buffer
++ len = strlen(result_buffer);
++ memcpy(page, result_buffer, len + 1);
++
++ // initialize the result buffer for later
++ strcpy(result_buffer, "not called");
++
++ return len;
++}
++#endif
++
++/** module initialization function */
++static int __init init_acpi_call(void)
++{
++#ifdef HAVE_PROC_CREATE
++ struct proc_dir_entry *acpi_entry = proc_create("call",
++ 0660,
++ acpi_root_dir,
++ &proc_acpi_operations);
++#else
++ struct proc_dir_entry *acpi_entry = create_proc_entry("call", 0660, acpi_root_dir);
++#endif
++
++ strcpy(result_buffer, "not called");
++
++ if (acpi_entry == NULL) {
++ printk(KERN_ERR "acpi_call: Couldn't create proc entry\n");
++ return -ENOMEM;
++ }
++
++#ifndef HAVE_PROC_CREATE
++ acpi_entry->write_proc = acpi_proc_write;
++ acpi_entry->read_proc = acpi_proc_read;
++#endif
++
++#ifdef DEBUG
++ printk(KERN_INFO "acpi_call: Module loaded successfully\n");
++#endif
++
++ return 0;
++}
++
++static void __exit unload_acpi_call(void)
++{
++ remove_proc_entry("call", acpi_root_dir);
++
++#ifdef DEBUG
++ printk(KERN_INFO "acpi_call: Module unloaded successfully\n");
++#endif
++}
++
++module_init(init_acpi_call);
++module_exit(unload_acpi_call);
+\ No newline at end of file
+--
+2.42.0
+
diff --git a/SOURCES/0001-amd-hdr.patch b/SOURCES/0001-amd-hdr.patch
index 50e988e..7e0717f 100644
--- a/SOURCES/0001-amd-hdr.patch
+++ b/SOURCES/0001-amd-hdr.patch
@@ -1,6 +1,6 @@
-From af60f9afa522f5f337d9b4e24eef1fdcd0ab6c05 Mon Sep 17 00:00:00 2001
+From 414938db7a335d0ba6579abf402cfaafdc98bae7 Mon Sep 17 00:00:00 2001
From: Peter Jung <admin@ptr1337.dev>
-Date: Mon, 11 Sep 2023 14:31:43 +0200
+Date: Wed, 29 Nov 2023 19:55:12 +0100
Subject: [PATCH 1/7] amd-hdr
Signed-off-by: Peter Jung <admin@ptr1337.dev>
@@ -27,7 +27,7 @@ Signed-off-by: Peter Jung <admin@ptr1337.dev>
19 files changed, 1441 insertions(+), 90 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
-index 32fe05c810c6..84bf501b02f4 100644
+index 32fe05c810c6f..84bf501b02f4c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
@@ -343,6 +343,77 @@ struct amdgpu_mode_info {
@@ -109,10 +109,10 @@ index 32fe05c810c6..84bf501b02f4 100644
#define AMDGPU_MAX_BL_LEVEL 0xFF
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
-index 34f011cedd06..fb3400eff0b6 100644
+index deedcd9978459..572c71a54df42 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
-@@ -4021,6 +4021,11 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
+@@ -4022,6 +4022,11 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
return r;
}
@@ -124,7 +124,7 @@ index 34f011cedd06..fb3400eff0b6 100644
r = amdgpu_dm_audio_init(adev);
if (r) {
dc_release_state(state->context);
-@@ -5093,7 +5098,9 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
+@@ -5094,7 +5099,9 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
* Always set input transfer function, since plane state is refreshed
* every time.
*/
@@ -135,7 +135,7 @@ index 34f011cedd06..fb3400eff0b6 100644
if (ret)
return ret;
-@@ -8113,6 +8120,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+@@ -8117,6 +8124,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
bundle->surface_updates[planes_count].gamma = dc_plane->gamma_correction;
bundle->surface_updates[planes_count].in_transfer_func = dc_plane->in_transfer_func;
bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix;
@@ -146,7 +146,7 @@ index 34f011cedd06..fb3400eff0b6 100644
}
amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state,
-@@ -8324,6 +8335,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+@@ -8328,6 +8339,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
&acrtc_state->stream->csc_color_matrix;
bundle->stream_update.out_transfer_func =
acrtc_state->stream->out_transfer_func;
@@ -157,7 +157,7 @@ index 34f011cedd06..fb3400eff0b6 100644
}
acrtc_state->stream->abm_level = acrtc_state->abm_level;
-@@ -9512,6 +9527,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
+@@ -9516,6 +9531,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
* when a modeset is needed, to ensure it gets reprogrammed.
*/
if (dm_new_crtc_state->base.color_mgmt_changed ||
@@ -165,7 +165,7 @@ index 34f011cedd06..fb3400eff0b6 100644
drm_atomic_crtc_needs_modeset(new_crtc_state)) {
ret = amdgpu_dm_update_crtc_color_mgmt(dm_new_crtc_state);
if (ret)
-@@ -9579,6 +9595,10 @@ static bool should_reset_plane(struct drm_atomic_state *state,
+@@ -9583,6 +9599,10 @@ static bool should_reset_plane(struct drm_atomic_state *state,
*/
for_each_oldnew_plane_in_state(state, other, old_other_state, new_other_state, i) {
struct amdgpu_framebuffer *old_afb, *new_afb;
@@ -176,7 +176,7 @@ index 34f011cedd06..fb3400eff0b6 100644
if (other->type == DRM_PLANE_TYPE_CURSOR)
continue;
-@@ -9615,6 +9635,18 @@ static bool should_reset_plane(struct drm_atomic_state *state,
+@@ -9619,6 +9639,18 @@ static bool should_reset_plane(struct drm_atomic_state *state,
old_other_state->color_encoding != new_other_state->color_encoding)
return true;
@@ -196,7 +196,7 @@ index 34f011cedd06..fb3400eff0b6 100644
if (!old_other_state->fb || !new_other_state->fb)
continue;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
-index 9e4cc5eeda76..24c87f425afb 100644
+index 9e4cc5eeda767..24c87f425afbb 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -33,6 +33,8 @@
@@ -339,7 +339,7 @@ index 9e4cc5eeda76..24c87f425afb 100644
void amdgpu_dm_update_connector_after_detect(
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
-index a4cb23d059bd..0442eeaa9763 100644
+index a4cb23d059bd6..0442eeaa97637 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -72,6 +72,7 @@
@@ -1285,7 +1285,7 @@ index a4cb23d059bd..0442eeaa9763 100644
+ dc_plane_state, color_caps);
+}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
-index 97b7a0b8a1c2..a05c210754d4 100644
+index 97b7a0b8a1c26..a05c210754d44 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -260,6 +260,7 @@ static struct drm_crtc_state *dm_crtc_duplicate_state(struct drm_crtc *crtc)
@@ -1389,7 +1389,7 @@ index 97b7a0b8a1c2..a05c210754d4 100644
fail:
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
-index cc74dd69acf2..17719e15cbe5 100644
+index cc74dd69acf2b..17719e15cbe58 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
@@ -1333,8 +1333,14 @@ static void dm_drm_plane_reset(struct drm_plane *plane)
@@ -1658,7 +1658,7 @@ index cc74dd69acf2..17719e15cbe5 100644
if (plane->funcs->reset)
plane->funcs->reset(plane);
diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c
-index 3538973bd0c6..04b2e04b68f3 100644
+index 3538973bd0c6c..04b2e04b68f33 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c
@@ -349,20 +349,37 @@ bool cm_helper_translate_curve_to_hw_format(struct dc_context *ctx,
@@ -1776,7 +1776,7 @@ index 3538973bd0c6..04b2e04b68f3 100644
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
-index 255713ec29bb..fce9b33c0f88 100644
+index 255713ec29bb0..fce9b33c0f881 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
@@ -186,6 +186,43 @@ bool dcn30_set_input_transfer_func(struct dc *dc,
@@ -1824,7 +1824,7 @@ index 255713ec29bb..fce9b33c0f88 100644
struct pipe_ctx *pipe_ctx,
const struct dc_stream_state *stream)
diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h
-index ce19c54097f8..e557e2b98618 100644
+index ce19c54097f8b..e557e2b986187 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h
@@ -58,6 +58,9 @@ bool dcn30_set_blend_lut(struct pipe_ctx *pipe_ctx,
@@ -1838,7 +1838,7 @@ index ce19c54097f8..e557e2b98618 100644
struct pipe_ctx *pipe_ctx,
const struct dc_stream_state *stream);
diff --git a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
-index 61205cdbe2d5..fdbe3d42cd7b 100644
+index 61205cdbe2d5a..fdbe3d42cd7b6 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
@@ -33,7 +33,7 @@
@@ -1851,7 +1851,7 @@ index 61205cdbe2d5..fdbe3d42cd7b 100644
.power_down_on_boot = dcn10_power_down_on_boot,
.apply_ctx_to_hw = dce110_apply_ctx_to_hw,
diff --git a/drivers/gpu/drm/amd/display/include/fixed31_32.h b/drivers/gpu/drm/amd/display/include/fixed31_32.h
-index d4cf7ead1d87..84da1dd34efd 100644
+index d4cf7ead1d877..84da1dd34efd1 100644
--- a/drivers/gpu/drm/amd/display/include/fixed31_32.h
+++ b/drivers/gpu/drm/amd/display/include/fixed31_32.h
@@ -69,6 +69,18 @@ static const struct fixed31_32 dc_fixpt_epsilon = { 1LL };
@@ -1874,7 +1874,7 @@ index d4cf7ead1d87..84da1dd34efd 100644
* @brief
* Initialization routines
diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
-index dc01c43f6193..d72c22dcf685 100644
+index dc01c43f61930..d72c22dcf6855 100644
--- a/drivers/gpu/drm/arm/malidp_crtc.c
+++ b/drivers/gpu/drm/arm/malidp_crtc.c
@@ -221,7 +221,7 @@ static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
@@ -1887,7 +1887,7 @@ index dc01c43f6193..d72c22dcf685 100644
ctm = (struct drm_color_ctm *)state->ctm->data;
for (i = 0; i < ARRAY_SIZE(ctm->matrix); ++i) {
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
-index c277b198fa3f..c3df45f90145 100644
+index c277b198fa3fa..c3df45f901456 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -733,6 +733,7 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
@@ -1899,7 +1899,7 @@ index c277b198fa3f..c3df45f90145 100644
if (plane->funcs->atomic_print_state)
plane->funcs->atomic_print_state(p, state);
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
-index 784e63d70a42..25bb0859fda7 100644
+index 784e63d70a421..25bb0859fda74 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -338,6 +338,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
@@ -1911,7 +1911,7 @@ index 784e63d70a42..25bb0859fda7 100644
EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c
-index dfec479830e4..f72ef6493340 100644
+index dfec479830e49..f72ef6493340a 100644
--- a/drivers/gpu/drm/drm_property.c
+++ b/drivers/gpu/drm/drm_property.c
@@ -751,6 +751,55 @@ bool drm_property_replace_blob(struct drm_property_blob **blob,
@@ -1971,7 +1971,7 @@ index dfec479830e4..f72ef6493340 100644
void *data, struct drm_file *file_priv)
{
diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h
-index 912f1e415685..08d7a7f0188f 100644
+index 912f1e4156853..08d7a7f0188fe 100644
--- a/include/drm/drm_mode_object.h
+++ b/include/drm/drm_mode_object.h
@@ -60,7 +60,7 @@ struct drm_mode_object {
@@ -1984,7 +1984,7 @@ index 912f1e415685..08d7a7f0188f 100644
* struct drm_object_properties - property tracking for &drm_mode_object
*/
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
-index 79d62856defb..4f87803b3ea1 100644
+index 79d62856defbf..4f87803b3ea12 100644
--- a/include/drm/drm_plane.h
+++ b/include/drm/drm_plane.h
@@ -237,6 +237,13 @@ struct drm_plane_state {
@@ -2002,7 +2002,7 @@ index 79d62856defb..4f87803b3ea1 100644
static inline struct drm_rect
diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h
-index 65bc9710a470..082f29156b3e 100644
+index 65bc9710a4702..082f29156b3e3 100644
--- a/include/drm/drm_property.h
+++ b/include/drm/drm_property.h
@@ -279,6 +279,12 @@ struct drm_property_blob *drm_property_create_blob(struct drm_device *dev,
@@ -2019,7 +2019,7 @@ index 65bc9710a470..082f29156b3e 100644
struct drm_property_blob **replace,
size_t length,
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
-index ea1b639bcb28..cea5653e4020 100644
+index ea1b639bcb288..cea5653e4020b 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -846,6 +846,14 @@ struct drm_color_ctm {
@@ -2038,5 +2038,5 @@ index ea1b639bcb28..cea5653e4020 100644
/*
* Values are mapped linearly to 0.0 - 1.0 range, with 0x0 == 0.0 and
--
-2.43.0.rc2
+2.43.0
diff --git a/SOURCES/0001-hid-asus-nero-patches-rogue.patch b/SOURCES/0001-hid-asus-nero-patches-rogue.patch
new file mode 100644
index 0000000..056cd95
--- /dev/null
+++ b/SOURCES/0001-hid-asus-nero-patches-rogue.patch
@@ -0,0 +1,940 @@
+diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
+index fd61dba88..3220d96fc 100644
+--- a/drivers/hid/hid-asus.c
++++ b/drivers/hid/hid-asus.c
+@@ -26,7 +26,9 @@
+ #include <linux/dmi.h>
+ #include <linux/hid.h>
+ #include <linux/module.h>
++#include <linux/sysfs.h>
+ #include <linux/platform_data/x86/asus-wmi.h>
++#include <linux/platform_device.h>
+ #include <linux/input/mt.h>
+ #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
+ #include <linux/power_supply.h>
+@@ -94,6 +96,435 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
+
+ #define TRKID_SGN ((TRKID_MAX + 1) >> 1)
+
++/*
++ * USB buffers to be used in a control transfer to make the joystick change buttons mode and scancodes
++ * 0 is default (game_mode with back buttons sending F17 and F18 instead of F15 for both as when unconfigured)
++ * 1 is mouse mode: back buttons still are F17 and F18
++ * 2 is macro mode
++ */
++static const u8 rc71l_mode_switch_commands[][23][64] = {
++ {
++ {
++ 0x5A, 0xD1, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x01, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x02, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x03, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x04, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x05, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x06, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x07, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x08, 0x2C, 0x02, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x09, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0F, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x06, 0x02, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x04, 0x04, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x05, 0x04, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ }
++ },
++ {
++ {
++ 0x5A, 0xD1, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x01, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x02, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x03, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x04, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x05, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x06, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x07, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x08, 0x2C, 0x02, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x09, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0F, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x06, 0x02, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x04, 0x04, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x05, 0x04, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ }
++ },
++ {
++ {
++ 0x5A, 0xD1, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x01, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x02, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x03, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x04, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x05, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x06, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x07, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x08, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x02, 0x09, 0x2C, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x0F, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x06, 0x02, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x04, 0x04, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ },
++ {
++ 0x5A, 0xD1, 0x05, 0x04, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ }
++ }
++};
++
+ struct asus_kbd_leds {
+ struct led_classdev cdev;
+ struct hid_device *hdev;
+@@ -103,6 +534,25 @@ struct asus_kbd_leds {
+ bool removed;
+ };
+
++enum rc71l_controller_mode {
++ rc71l_gamepad_mode,
++ rc71l_mouse_mode,
++ rc71l_macro_mode,
++};
++
++struct asus_rc71l {
++ unsigned int usb_pipe;
++
++ struct platform_device *mcu_dev;
++
++ struct mutex mutex; /* Mutex that protects everything below it */
++
++ enum rc71l_controller_mode mode;
++
++ u8 usb_in_buf[32];
++ u8 usb_out_buf[64]; /* A temporary buffer to hold data that gets sent over USB (must be accessed upon locking the appropriate mutex) */
++};
++
+ struct asus_touchpad_info {
+ int max_x;
+ int max_y;
+@@ -127,6 +577,7 @@ struct asus_drvdata {
+ int battery_stat;
+ bool battery_in_query;
+ unsigned long battery_next_query;
++ struct asus_rc71l *rc71l_data;
+ };
+
+ static int asus_report_battery(struct asus_drvdata *, u8 *, int);
+@@ -189,6 +640,245 @@ static const struct asus_touchpad_info medion_e1239t_tp = {
+ .report_size = 32 /* 2 byte header + 5 * 5 + 5 byte footer */,
+ };
+
++/**
++ * This function reads data over the USB device on the ROG Ally.
++ * Unlike outgoing traffic the inbound always performs 32-bytes transfers.
++ *
++ * PRE:
++ * - rc71l internal mutex MUST be locked
++ */
++static int rc71l_usb_read(struct hid_device * hdev) {
++ struct asus_drvdata *drvdata = (struct asus_drvdata*)hid_get_drvdata(hdev);
++ if (drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ struct asus_rc71l *rc71l_drvdata = drvdata->rc71l_data;
++ if (rc71l_drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
++ struct usb_device *dev = interface_to_usbdev(intf);
++
++ const int retval = usb_control_msg_recv(dev, 0x80, 0x01, 0xa1, 0x035A, 0x0002, (void*)&rc71l_drvdata->usb_in_buf[0], 32, 250, GFP_KERNEL);
++
++ if (retval < 0) {
++ hid_err(hdev, "Ally read failed performing control read, error %d\n", retval);
++ goto rc71l_usb_read_err;
++ }
++
++ const char* b = (const u8*)&rc71l_drvdata->usb_in_buf[0];
++ hid_info(hdev, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
++ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9],
++ b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19],
++ b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29],
++ b[30], b[31]
++ );
++
++rc71l_usb_read_err:
++ return retval;
++}
++
++/**
++ * This function writes a command over the USB device on the ROG Ally.
++ * The ROG Ally accepts 64-bytes long messages as commands: as such at most 64-bytes will be sent
++ * and unused bytes will be zeroed out.
++ *
++ * PRE:
++ * - rc71l internal mutex MUST be locked
++ */
++static int rc71l_usb_write(struct hid_device * hdev, const void* buf, size_t buf_sz) {
++ struct asus_drvdata *drvdata = (struct asus_drvdata*)hid_get_drvdata(hdev);
++
++ if (drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ struct asus_rc71l *rc71l_drvdata = drvdata->rc71l_data;
++ if (rc71l_drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
++ struct usb_device *dev = interface_to_usbdev(intf);
++
++ if (buf_sz > 64) {
++ hid_err(hdev, "Bug in the kernel: cannot write more than 64-bytes\n");
++
++ return -EINVAL;
++ }
++
++ // make sure bytes in excess will be zeroes and copy the user-provided buffer
++ memset((void*)&rc71l_drvdata->usb_out_buf[0], 0, 64);
++ memcpy((void*)&rc71l_drvdata->usb_out_buf[0], buf, buf_sz);
++
++ /* send the data out the bulk port */
++ const int retval = usb_control_msg(dev, rc71l_drvdata->usb_pipe, 0x09, 0x21, 0x035A, 0x0002, (void*)&rc71l_drvdata->usb_out_buf[0], 64, 250);
++ if (retval < 0) {
++ hid_err(hdev,
++ "Failed submitting control write error %d\n", retval);
++
++ goto rc71l_usb_write_err;
++ }
++
++rc71l_usb_write_err:
++ return retval < 0 ? retval : 0;
++}
++
++static int rc71l_mode_change(struct hid_device * hdev, enum rc71l_controller_mode new_mode) {
++ struct asus_drvdata *drvdata = (struct asus_drvdata*)hid_get_drvdata(hdev);
++ if (drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ struct asus_rc71l *rc71l_drvdata = drvdata->rc71l_data;
++ if (rc71l_drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ int ret = 0;
++
++ size_t packets_group = 0;
++ switch (new_mode) {
++ case rc71l_gamepad_mode:
++ packets_group = 0;
++ break;
++
++ case rc71l_mouse_mode:
++ packets_group = 1;
++ break;
++
++ case rc71l_macro_mode:
++ packets_group = 2;
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ for (int i = 0; (i < 23) && (ret == 0); ++i) {
++ ret = rc71l_usb_write(hdev, (const void*)&rc71l_mode_switch_commands[packets_group][i][0], 64);
++ if (ret > 0) {
++ hid_err(hdev, "Ally controller mode switch %d/23 error %d\n", i, ret);
++ goto rc71l_mode_change_err;
++ }
++ }
++
++ // controller mode has been switched successfully: change that in driver data
++ if (ret == 0) {
++ hid_info(hdev, "ROG Ally [RC71L] controller mode switch succeeded\n");
++ rc71l_drvdata->mode = new_mode;
++ }
++
++rc71l_mode_change_err:
++ return ret;
++}
++
++static ssize_t __maybe_unused mode_show(struct device *raw_dev, struct device_attribute *attr, char *buf) {
++ struct platform_device *const pdev = to_platform_device(raw_dev);
++ struct hid_device *const hdev = platform_get_drvdata(pdev);
++ if (hdev == NULL) {
++ return -EINVAL;
++ }
++
++ struct asus_drvdata *const drvdata = (struct asus_drvdata*)hid_get_drvdata(hdev);
++ if (drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ struct asus_rc71l *const rc71l_drvdata = drvdata->rc71l_data;
++ if (rc71l_drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ mutex_lock(&rc71l_drvdata->mutex);
++ int current_mode = 0;
++ switch (rc71l_drvdata->mode) {
++ case rc71l_gamepad_mode:
++ current_mode = 0;
++ break;
++
++ case rc71l_mouse_mode:
++ current_mode = 1;
++ break;
++
++ case rc71l_macro_mode:
++ current_mode = 2;
++ break;
++
++ default:
++ mutex_unlock(&rc71l_drvdata->mutex);
++ return -EINVAL;
++ }
++ mutex_unlock(&rc71l_drvdata->mutex);
++
++ return sysfs_emit(buf, "%d\n", (int)current_mode);
++}
++
++static ssize_t __maybe_unused mode_store(struct device *raw_dev, struct device_attribute *attr, const char *buf, size_t count) {
++ struct platform_device *const pdev = to_platform_device(raw_dev);
++ struct hid_device *const hdev = platform_get_drvdata(pdev);
++ if (hdev == NULL) {
++ return -EINVAL;
++ }
++
++ struct asus_drvdata *const drvdata = (struct asus_drvdata*)hid_get_drvdata(hdev);
++ if (drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ struct asus_rc71l *const rc71l_drvdata = drvdata->rc71l_data;
++ if (rc71l_drvdata == NULL) {
++ return -EINVAL;
++ }
++
++ int res = -EINVAL;
++ int val = -EINVAL;
++ res = kstrtoint(buf, 0, &val);
++ if (res)
++ return res;
++
++ switch (val) {
++ case 0:
++ mutex_lock(&rc71l_drvdata->mutex);
++ res = rc71l_mode_change(hdev, rc71l_gamepad_mode);
++ mutex_unlock(&rc71l_drvdata->mutex);
++ break;
++
++ case 1:
++ mutex_lock(&rc71l_drvdata->mutex);
++ res = rc71l_mode_change(hdev, rc71l_mouse_mode);
++ mutex_unlock(&rc71l_drvdata->mutex);
++ break;
++
++ case 2:
++ mutex_lock(&rc71l_drvdata->mutex);
++ res = rc71l_mode_change(hdev, rc71l_macro_mode);
++ mutex_unlock(&rc71l_drvdata->mutex);
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ hid_err(hdev, "Ally controller mode switch to %d mode op result: %d\n", val, res);
++
++ return count;
++}
++
++DEVICE_ATTR_RW(mode);
++
++static struct attribute *rc71l_input_attrs[] = {
++ &dev_attr_mode.attr,
++ NULL
++};
++
++static const struct attribute_group mcu_attr_group = {
++ .name = "input",
++ .attrs = rc71l_input_attrs,
++};
++
+ static void asus_report_contact_down(struct asus_drvdata *drvdat,
+ int toolType, u8 *data)
+ {
+@@ -386,7 +1076,7 @@ static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size
+ unsigned char *dmabuf;
+ int ret;
+
+- dmabuf = kmemdup(buf, buf_size, GFP_KERNEL);
++ dmabuf = kmemdup((const void*)buf, buf_size, GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+@@ -1000,16 +1694,108 @@ static int asus_start_multitouch(struct hid_device *hdev)
+ return 0;
+ }
+
++#ifdef CONFIG_PM
+ static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
+ {
++ int ret = 0;
++
+ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
++ if (drvdata != NULL) {
++ return -EINVAL;
++ }
+
+ if (drvdata->tp)
+ return asus_start_multitouch(hdev);
+
+- return 0;
++ return ret;
+ }
+
++static int __maybe_unused asus_resume(struct hid_device *hdev)
++{
++ int ret = 0;
++ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
++/*
++ // Controller mode is kept on device sleep
++ if (dmi_match(DMI_PRODUCT_NAME, "ROG Ally RC71L_RC71L"))
++ {
++ // Apply the joystick mode switch
++ ret = rog_ally_controller_mode_change(hdev, game_mode);
++
++ hid_err(hdev, "Asus wake, restore controller %d\n", ret);
++ }
++*/
++
++ struct asus_rc71l *rc71l_drvdata = drvdata->rc71l_data;
++ if (rc71l_drvdata != NULL) {
++ mutex_lock(&rc71l_drvdata->mutex);
++ ret = rc71l_mode_change(hdev, rc71l_drvdata->mode);
++ mutex_unlock(&rc71l_drvdata->mutex);
++
++ if (ret < 0) {
++ hid_err(hdev, "ROG Ally [RC71L] failed to reset controller mode: %d\n", ret);
++ goto asus_resume_err;
++ }
++ }
++
++
++ /*
++ * On some devices such as the Asus RC71L leds are reset to default after sleep and sysfs attribute will report
++ * something that won't be true: resetting the user-provided value is necessary to maintain coherency and avoid
++ * flashing full brightness leds in face of the user.
++ */
++ if (drvdata->kbd_backlight) {
++ const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, drvdata->kbd_backlight->cdev.brightness };
++ ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
++ if (ret < 0) {
++ hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret);
++ goto asus_resume_err;
++ }
++
++ hid_err(hdev, "Asus ROG Ally asus_reset_resume, leds reset: %d at brightness %d\n", ret, (int)drvdata->kbd_backlight->cdev.brightness);
++ }
++
++ asus_resume_err:
++ return ret;
++}
++
++static int __maybe_unused asus_suspend(struct hid_device *hdev, struct pm_message)
++ {
++ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
++
++ if (drvdata == NULL) {
++ return 0;
++ }
++
++ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
++ struct usb_device *dev = interface_to_usbdev(intf);
++
++ int ret = 0;
++
++ if (dmi_match(DMI_PRODUCT_NAME, "ROG Ally RC71L_RC71L")) {
++ // Send the USB ABORT_PIPE command
++ int result = usb_control_msg(
++ dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE,
++ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT,
++ USB_ENDPOINT_HALT, 0x02, NULL, 0, 1000);
++
++ if (result < 0) {
++ printk("USB ABORT_PIPE failed: %d\n", result);
++ } else {
++ printk("USB ABORT_PIPE succeeded\n");
++ }
++ }
++
++ struct asus_rc71l *rc71l_drvdata = drvdata->rc71l_data;
++ if (rc71l_drvdata != NULL) {
++ mutex_lock(&rc71l_drvdata->mutex);
++ // TODO: send ABORT_PIPE here
++ mutex_unlock(&rc71l_drvdata->mutex);
++ }
++
++ return ret;
++}
++#endif
++
+ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
+ {
+ int ret;
+@@ -1021,6 +1807,8 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
+ return -ENOMEM;
+ }
+
++ drvdata->rc71l_data = NULL;
++
+ hid_set_drvdata(hdev, drvdata);
+
+ drvdata->quirks = id->driver_data;
+@@ -1109,6 +1897,51 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
+ goto err_stop_hw;
+ }
+
++ if ((dmi_match(DMI_PRODUCT_NAME, "ROG Ally RC71L_RC71L")) && (hdev->rsize > 9) && (hdev->rdesc[7] == 0x85) && (hdev->rdesc[8] == 0x5a))
++ {
++ drvdata->rc71l_data = devm_kzalloc(&hdev->dev, sizeof(*drvdata->rc71l_data), GFP_KERNEL);
++ if (drvdata->rc71l_data == NULL) {
++ hid_err(hdev, "Can't alloc Asus ROG Ally [RC71L] descriptor\n");
++ ret = -ENOMEM;
++ goto err_stop_hw;
++ }
++
++ mutex_init(&drvdata->rc71l_data->mutex);
++
++ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
++ struct usb_device *dev = interface_to_usbdev(intf);
++
++ // default controller mode
++ drvdata->rc71l_data->mode = rc71l_gamepad_mode;
++
++ // usb_device and endpoint
++ drvdata->rc71l_data->usb_pipe = usb_sndctrlpipe(dev, 0);
++
++ // apply the default controller mode
++ mutex_lock(&drvdata->rc71l_data->mutex);
++ ret = rc71l_mode_change(hdev, drvdata->rc71l_data->mode);
++ mutex_unlock(&drvdata->rc71l_data->mutex);
++
++ if (ret < 0) {
++ hid_err(hdev, "Asus ROG Ally [RC71L] error setting the default controller mode: %d\n", ret);
++ goto err_stop_hw;
++ }
++
++ drvdata->rc71l_data->mcu_dev = platform_device_register_simple("asus-mcu", 0, NULL, 0);
++ if (IS_ERR(drvdata->rc71l_data->mcu_dev)) {
++ hid_err(hdev, "Error registering MCU platform device: %ld\n", PTR_ERR(drvdata->rc71l_data->mcu_dev));
++ goto err_stop_hw;
++ }
++
++ platform_set_drvdata(drvdata->rc71l_data->mcu_dev, hdev);
++
++ ret = devm_device_add_group(&drvdata->rc71l_data->mcu_dev->dev, &mcu_attr_group);
++ if (ret != 0) {
++ platform_device_unregister(drvdata->rc71l_data->mcu_dev);
++ goto err_stop_hw;
++ }
++ }
++
+ if (drvdata->tp) {
+ drvdata->input->name = "Asus TouchPad";
+ } else {
+@@ -1140,6 +1973,16 @@ static void asus_remove(struct hid_device *hdev)
+ cancel_work_sync(&drvdata->kbd_backlight->work);
+ }
+
++ struct asus_rc71l *rc71l_drvdata = drvdata->rc71l_data;
++ if (rc71l_drvdata != NULL) {
++ platform_device_unregister(rc71l_drvdata->mcu_dev);
++
++ mutex_lock(&rc71l_drvdata->mutex);
++ platform_device_unregister(rc71l_drvdata->mcu_dev);
++ // TODO: perform cleanup operations
++ mutex_unlock(&rc71l_drvdata->mutex);
++ }
++
+ hid_hw_stop(hdev);
+ }
+
+@@ -1294,6 +2140,8 @@ static struct hid_driver asus_driver = {
+ .input_configured = asus_input_configured,
+ #ifdef CONFIG_PM
+ .reset_resume = asus_reset_resume,
++ .resume = asus_resume,
++ .suspend = asus_suspend,
+ #endif
+ .event = asus_event,
+ .raw_event = asus_raw_event
+
+--
+2.43.0
+
diff --git a/SOURCES/ROG-ALLY-NCT6775-PLATFORM.patch b/SOURCES/ROG-ALLY-NCT6775-PLATFORM.patch
new file mode 100644
index 0000000..806ad79
--- /dev/null
+++ b/SOURCES/ROG-ALLY-NCT6775-PLATFORM.patch
@@ -0,0 +1,12 @@
+diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c
+index 81bf03d..96d875b 100644
+--- a/drivers/hwmon/nct6775-platform.c
++++ b/drivers/hwmon/nct6775-platform.c
+@@ -1359,6 +1359,7 @@ static const char * const asus_msi_boards[] = {
+ "ProArt X670E-CREATOR WIFI",
+ "ProArt Z690-CREATOR WIFI",
+ "ProArt Z790-CREATOR WIFI",
++ "RC71L",
+ "ROG CROSSHAIR X670E EXTREME",
+ "ROG CROSSHAIR X670E GENE",
+ "ROG CROSSHAIR X670E HERO",
diff --git a/SOURCES/acso.patch b/SOURCES/acso.patch
deleted file mode 100644
index 424fab4..0000000
--- a/SOURCES/acso.patch
+++ /dev/null
@@ -1,195 +0,0 @@
-From fd848b79a1acbe986bcf604826c2fa5fab06566e Mon Sep 17 00:00:00 2001
-From: Mark Weiman <mark.weiman@markzz.com>
-Date: Sun, 12 Aug 2018 11:36:21 -0400
-Subject: [PATCH 03/36] pci: Enable overrides for missing ACS capabilities
-
-This an updated version of Alex Williamson's patch from:
-https://lkml.org/lkml/2013/5/30/513
-
-Original commit message follows:
-
-PCIe ACS (Access Control Services) is the PCIe 2.0+ feature that
-allows us to control whether transactions are allowed to be redirected
-in various subnodes of a PCIe topology. For instance, if two
-endpoints are below a root port or downsteam switch port, the
-downstream port may optionally redirect transactions between the
-devices, bypassing upstream devices. The same can happen internally
-on multifunction devices. The transaction may never be visible to the
-upstream devices.
-
-One upstream device that we particularly care about is the IOMMU. If
-a redirection occurs in the topology below the IOMMU, then the IOMMU
-cannot provide isolation between devices. This is why the PCIe spec
-encourages topologies to include ACS support. Without it, we have to
-assume peer-to-peer DMA within a hierarchy can bypass IOMMU isolation.
-
-Unfortunately, far too many topologies do not support ACS to make this
-a steadfast requirement. Even the latest chipsets from Intel are only
-sporadically supporting ACS. We have trouble getting interconnect
-vendors to include the PCIe spec required PCIe capability, let alone
-suggested features.
-
-Therefore, we need to add some flexibility. The pcie_acs_override=
-boot option lets users opt-in specific devices or sets of devices to
-assume ACS support. The "downstream" option assumes full ACS support
-on root ports and downstream switch ports. The "multifunction"
-option assumes the subset of ACS features available on multifunction
-endpoints and upstream switch ports are supported. The "id:nnnn:nnnn"
-option enables ACS support on devices matching the provided vendor
-and device IDs, allowing more strategic ACS overrides. These options
-may be combined in any order. A maximum of 16 id specific overrides
-are available. It's suggested to use the most limited set of options
-necessary to avoid completely disabling ACS across the topology.
-Note to hardware vendors, we have facilities to permanently quirk
-specific devices which enforce isolation but not provide an ACS
-capability. Please contact me to have your devices added and save
-your customers the hassle of this boot option.
-
-Signed-off-by: Mark Weiman <mark.weiman@markzz.com>
-Signed-off-by: Alexandre Frade <admfrade@gmail.com>
----
- .../admin-guide/kernel-parameters.txt | 8 ++
- drivers/pci/quirks.c | 102 ++++++++++++++++++
- 2 files changed, 110 insertions(+)
-
-diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
-index 6cfa6e3996cf..fdbe34c0fbf3 100644
---- a/Documentation/admin-guide/kernel-parameters.txt
-+++ b/Documentation/admin-guide/kernel-parameters.txt
-@@ -4178,6 +4178,14 @@
- nomsi [MSI] If the PCI_MSI kernel config parameter is
- enabled, this kernel boot option can be used to
- disable the use of MSI interrupts system-wide.
-+ pci_acs_override [PCIE] Override missing PCIe ACS support for:
-+ downstream
-+ All downstream ports - full ACS capabilities
-+ multifunction
-+ Add multifunction devices - multifunction ACS subset
-+ id:nnnn:nnnn
-+ Specific device - full ACS capabilities
-+ Specified as vid:did (vendor/device ID) in hex
- noioapicquirk [APIC] Disable all boot interrupt quirks.
- Safety option to keep boot IRQs enabled. This
- should never be necessary.
-diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
-index 494fa46f5767..41150eb8bd4c 100644
---- a/drivers/pci/quirks.c
-+++ b/drivers/pci/quirks.c
-@@ -194,6 +194,106 @@ static int __init pci_apply_final_quirks(void)
- }
- fs_initcall_sync(pci_apply_final_quirks);
-
-+static bool acs_on_downstream;
-+static bool acs_on_multifunction;
-+
-+#define NUM_ACS_IDS 16
-+struct acs_on_id {
-+ unsigned short vendor;
-+ unsigned short device;
-+};
-+static struct acs_on_id acs_on_ids[NUM_ACS_IDS];
-+static u8 max_acs_id;
-+
-+static __init int pcie_acs_override_setup(char *p)
-+{
-+ if (!p)
-+ return -EINVAL;
-+
-+ while (*p) {
-+ if (!strncmp(p, "downstream", 10))
-+ acs_on_downstream = true;
-+ if (!strncmp(p, "multifunction", 13))
-+ acs_on_multifunction = true;
-+ if (!strncmp(p, "id:", 3)) {
-+ char opt[5];
-+ int ret;
-+ long val;
-+
-+ if (max_acs_id >= NUM_ACS_IDS - 1) {
-+ pr_warn("Out of PCIe ACS override slots (%d)\n",
-+ NUM_ACS_IDS);
-+ goto next;
-+ }
-+
-+ p += 3;
-+ snprintf(opt, 5, "%s", p);
-+ ret = kstrtol(opt, 16, &val);
-+ if (ret) {
-+ pr_warn("PCIe ACS ID parse error %d\n", ret);
-+ goto next;
-+ }
-+ acs_on_ids[max_acs_id].vendor = val;
-+ p += strcspn(p, ":");
-+ if (*p != ':') {
-+ pr_warn("PCIe ACS invalid ID\n");
-+ goto next;
-+ }
-+
-+ p++;
-+ snprintf(opt, 5, "%s", p);
-+ ret = kstrtol(opt, 16, &val);
-+ if (ret) {
-+ pr_warn("PCIe ACS ID parse error %d\n", ret);
-+ goto next;
-+ }
-+ acs_on_ids[max_acs_id].device = val;
-+ max_acs_id++;
-+ }
-+next:
-+ p += strcspn(p, ",");
-+ if (*p == ',')
-+ p++;
-+ }
-+
-+ if (acs_on_downstream || acs_on_multifunction || max_acs_id)
-+ pr_warn("Warning: PCIe ACS overrides enabled; This may allow non-IOMMU protected peer-to-peer DMA\n");
-+
-+ return 0;
-+}
-+early_param("pcie_acs_override", pcie_acs_override_setup);
-+
-+static int pcie_acs_overrides(struct pci_dev *dev, u16 acs_flags)
-+{
-+ int i;
-+
-+ /* Never override ACS for legacy devices or devices with ACS caps */
-+ if (!pci_is_pcie(dev) ||
-+ pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS))
-+ return -ENOTTY;
-+
-+ for (i = 0; i < max_acs_id; i++)
-+ if (acs_on_ids[i].vendor == dev->vendor &&
-+ acs_on_ids[i].device == dev->device)
-+ return 1;
-+
-+switch (pci_pcie_type(dev)) {
-+ case PCI_EXP_TYPE_DOWNSTREAM:
-+ case PCI_EXP_TYPE_ROOT_PORT:
-+ if (acs_on_downstream)
-+ return 1;
-+ break;
-+ case PCI_EXP_TYPE_ENDPOINT:
-+ case PCI_EXP_TYPE_UPSTREAM:
-+ case PCI_EXP_TYPE_LEG_END:
-+ case PCI_EXP_TYPE_RC_END:
-+ if (acs_on_multifunction && dev->multifunction)
-+ return 1;
-+ }
-+
-+ return -ENOTTY;
-+}
-+
- /*
- * Decoding should be disabled for a PCI device during BAR sizing to avoid
- * conflict. But doing so may cause problems on host bridge and perhaps other
-@@ -5002,6 +5102,8 @@ static const struct pci_dev_acs_enabled {
- { PCI_VENDOR_ID_ZHAOXIN, PCI_ANY_ID, pci_quirk_zhaoxin_pcie_ports_acs },
- /* Wangxun nics */
- { PCI_VENDOR_ID_WANGXUN, PCI_ANY_ID, pci_quirk_wangxun_nic_acs },
-+ /* allow acs for any */
-+ { PCI_ANY_ID, PCI_ANY_ID, pcie_acs_overrides },
- { 0 }
- };
-
---
-2.39.2
diff --git a/SOURCES/amdgpu-bug-fix.patch b/SOURCES/amdgpu-bug-fix.patch
new file mode 100644
index 0000000..1e48174
--- /dev/null
+++ b/SOURCES/amdgpu-bug-fix.patch
@@ -0,0 +1,617 @@
+From 0af59135c2a9e05af87bc82f492fab13fff52fbd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Andr=C3=A9=20Almeida?= <andrealmeid@igalia.com>
+Date: Wed, 22 Nov 2023 13:19:38 -0300
+Subject: [PATCH] drm: Refuse to async flip with atomic prop changes
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Given that prop changes may lead to modesetting, which would defeat the
+fast path of the async flip, refuse any atomic prop change for async
+flips in atomic API. The only exception is the framebuffer ID to flip
+to. Currently the only plane type supported is the primary one.
+
+Signed-off-by: André Almeida <andrealmeid@igalia.com>
+Reviewed-by: Simon Ser <contact@emersion.fr>
+---
+ drivers/gpu/drm/drm_atomic_uapi.c | 52 +++++++++++++++++++++++++++--
+ drivers/gpu/drm/drm_crtc_internal.h | 2 +-
+ drivers/gpu/drm/drm_mode_object.c | 2 +-
+ 3 files changed, 51 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
+index 37caa6c33e22b3..86083184ac6bb2 100644
+--- a/drivers/gpu/drm/drm_atomic_uapi.c
++++ b/drivers/gpu/drm/drm_atomic_uapi.c
+@@ -964,13 +964,28 @@ int drm_atomic_connector_commit_dpms(struct drm_atomic_state *state,
+ return ret;
+ }
+
++static int drm_atomic_check_prop_changes(int ret, uint64_t old_val, uint64_t prop_value,
++ struct drm_property *prop)
++{
++ if (ret != 0 || old_val != prop_value) {
++ drm_dbg_atomic(prop->dev,
++ "[PROP:%d:%s] No prop can be changed during async flip\n",
++ prop->base.id, prop->name);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
+ int drm_atomic_set_property(struct drm_atomic_state *state,
+ struct drm_file *file_priv,
+ struct drm_mode_object *obj,
+ struct drm_property *prop,
+- uint64_t prop_value)
++ uint64_t prop_value,
++ bool async_flip)
+ {
+ struct drm_mode_object *ref;
++ uint64_t old_val;
+ int ret;
+
+ if (!drm_property_change_valid_get(prop, prop_value, &ref))
+@@ -987,6 +1002,13 @@ int drm_atomic_set_property(struct drm_atomic_state *state,
+ break;
+ }
+
++ if (async_flip) {
++ ret = drm_atomic_connector_get_property(connector, connector_state,
++ prop, &old_val);
++ ret = drm_atomic_check_prop_changes(ret, old_val, prop_value, prop);
++ break;
++ }
++
+ ret = drm_atomic_connector_set_property(connector,
+ connector_state, file_priv,
+ prop, prop_value);
+@@ -1002,6 +1024,13 @@ int drm_atomic_set_property(struct drm_atomic_state *state,
+ break;
+ }
+
++ if (async_flip) {
++ ret = drm_atomic_crtc_get_property(crtc, crtc_state,
++ prop, &old_val);
++ ret = drm_atomic_check_prop_changes(ret, old_val, prop_value, prop);
++ break;
++ }
++
+ ret = drm_atomic_crtc_set_property(crtc,
+ crtc_state, prop, prop_value);
+ break;
+@@ -1009,6 +1038,7 @@ int drm_atomic_set_property(struct drm_atomic_state *state,
+ case DRM_MODE_OBJECT_PLANE: {
+ struct drm_plane *plane = obj_to_plane(obj);
+ struct drm_plane_state *plane_state;
++ struct drm_mode_config *config = &plane->dev->mode_config;
+
+ plane_state = drm_atomic_get_plane_state(state, plane);
+ if (IS_ERR(plane_state)) {
+@@ -1016,6 +1046,21 @@ int drm_atomic_set_property(struct drm_atomic_state *state,
+ break;
+ }
+
++ if (async_flip && prop != config->prop_fb_id) {
++ ret = drm_atomic_plane_get_property(plane, plane_state,
++ prop, &old_val);
++ ret = drm_atomic_check_prop_changes(ret, old_val, prop_value, prop);
++ break;
++ }
++
++ if (async_flip && plane_state->plane->type != DRM_PLANE_TYPE_PRIMARY) {
++ drm_dbg_atomic(prop->dev,
++ "[OBJECT:%d] Only primary planes can be changed during async flip\n",
++ obj->id);
++ ret = -EINVAL;
++ break;
++ }
++
+ ret = drm_atomic_plane_set_property(plane,
+ plane_state, file_priv,
+ prop, prop_value);
+@@ -1295,6 +1340,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
+ struct drm_out_fence_state *fence_state;
+ int ret = 0;
+ unsigned int i, j, num_fences;
++ bool async_flip = false;
+
+ /* disallow for drivers not supporting atomic: */
+ if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
+@@ -1408,8 +1454,8 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
+ goto out;
+ }
+
+- ret = drm_atomic_set_property(state, file_priv,
+- obj, prop, prop_value);
++ ret = drm_atomic_set_property(state, file_priv, obj,
++ prop, prop_value, async_flip);
+ if (ret) {
+ drm_mode_object_put(obj);
+ goto out;
+diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
+index 501a10edd0e1dc..381130cebe811c 100644
+--- a/drivers/gpu/drm/drm_crtc_internal.h
++++ b/drivers/gpu/drm/drm_crtc_internal.h
+@@ -251,7 +251,7 @@ int drm_atomic_set_property(struct drm_atomic_state *state,
+ struct drm_file *file_priv,
+ struct drm_mode_object *obj,
+ struct drm_property *prop,
+- uint64_t prop_value);
++ uint64_t prop_value, bool async_flip);
+ int drm_atomic_get_property(struct drm_mode_object *obj,
+ struct drm_property *property, uint64_t *val);
+
+diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c
+index ac0d2ce3f87041..0e8355063eee36 100644
+--- a/drivers/gpu/drm/drm_mode_object.c
++++ b/drivers/gpu/drm/drm_mode_object.c
+@@ -538,7 +538,7 @@ static int set_property_atomic(struct drm_mode_object *obj,
+ obj_to_connector(obj),
+ prop_value);
+ } else {
+- ret = drm_atomic_set_property(state, file_priv, obj, prop, prop_value);
++ ret = drm_atomic_set_property(state, file_priv, obj, prop, prop_value, false);
+ if (ret)
+ goto out;
+ ret = drm_atomic_commit(state);
+From dd4a6dbc71451975ff33ad6f3fcf5f440f67be5b Mon Sep 17 00:00:00 2001
+From: Simon Ser <contact@emersion.fr>
+Date: Wed, 22 Nov 2023 13:19:39 -0300
+Subject: [PATCH] drm: allow DRM_MODE_PAGE_FLIP_ASYNC for atomic commits
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If the driver supports it, allow user-space to supply the
+DRM_MODE_PAGE_FLIP_ASYNC flag to request an async page-flip.
+Set drm_crtc_state.async_flip accordingly.
+
+Document that drivers will reject atomic commits if an async
+flip isn't possible. This allows user-space to fall back to
+something else. For instance, Xorg falls back to a blit.
+Another option is to wait as close to the next vblank as
+possible before performing the page-flip to reduce latency.
+
+Signed-off-by: Simon Ser <contact@emersion.fr>
+Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
+Co-developed-by: André Almeida <andrealmeid@igalia.com>
+Signed-off-by: André Almeida <andrealmeid@igalia.com>
+---
+ drivers/gpu/drm/drm_atomic_uapi.c | 25 ++++++++++++++++++++++---
+ include/uapi/drm/drm_mode.h | 9 +++++++++
+ 2 files changed, 31 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
+index 86083184ac6bb2..fd429c61a54ecd 100644
+--- a/drivers/gpu/drm/drm_atomic_uapi.c
++++ b/drivers/gpu/drm/drm_atomic_uapi.c
+@@ -1326,6 +1326,18 @@ static void complete_signaling(struct drm_device *dev,
+ kfree(fence_state);
+ }
+
++static void
++set_async_flip(struct drm_atomic_state *state)
++{
++ struct drm_crtc *crtc;
++ struct drm_crtc_state *crtc_state;
++ int i;
++
++ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
++ crtc_state->async_flip = true;
++ }
++}
++
+ int drm_mode_atomic_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+ {
+@@ -1367,9 +1379,13 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
+ }
+
+ if (arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) {
+- drm_dbg_atomic(dev,
+- "commit failed: invalid flag DRM_MODE_PAGE_FLIP_ASYNC\n");
+- return -EINVAL;
++ if (!dev->mode_config.async_page_flip) {
++ drm_dbg_atomic(dev,
++ "commit failed: DRM_MODE_PAGE_FLIP_ASYNC not supported\n");
++ return -EINVAL;
++ }
++
++ async_flip = true;
+ }
+
+ /* can't test and expect an event at the same time. */
+@@ -1472,6 +1488,9 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
+ if (ret)
+ goto out;
+
++ if (arg->flags & DRM_MODE_PAGE_FLIP_ASYNC)
++ set_async_flip(state);
++
+ if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
+ ret = drm_atomic_check_only(state);
+ } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
+diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
+index cea5653e4020b3..c0b76575f11787 100644
+--- a/include/uapi/drm/drm_mode.h
++++ b/include/uapi/drm/drm_mode.h
+@@ -965,6 +965,15 @@ struct hdr_output_metadata {
+ * Request that the page-flip is performed as soon as possible, ie. with no
+ * delay due to waiting for vblank. This may cause tearing to be visible on
+ * the screen.
++ *
++ * When used with atomic uAPI, the driver will return an error if the hardware
++ * doesn't support performing an asynchronous page-flip for this update.
++ * User-space should handle this, e.g. by falling back to a regular page-flip.
++ *
++ * Note, some hardware might need to perform one last synchronous page-flip
++ * before being able to switch to asynchronous page-flips. As an exception,
++ * the driver will return success even though that first page-flip is not
++ * asynchronous.
+ */
+ #define DRM_MODE_PAGE_FLIP_ASYNC 0x02
+ #define DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE 0x4
+From 631191c0d1ef5884b6c4ed760013da05ec3c0005 Mon Sep 17 00:00:00 2001
+From: Simon Ser <contact@emersion.fr>
+Date: Wed, 22 Nov 2023 13:19:40 -0300
+Subject: [PATCH] drm: introduce DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This new kernel capability indicates whether async page-flips are
+supported via the atomic uAPI. DRM clients can use it to check
+for support before feeding DRM_MODE_PAGE_FLIP_ASYNC to the kernel.
+
+Make it clear that DRM_CAP_ASYNC_PAGE_FLIP is for legacy uAPI only.
+
+Signed-off-by: Simon Ser <contact@emersion.fr>
+Reviewed-by: André Almeida <andrealmeid@igalia.com>
+Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: André Almeida <andrealmeid@igalia.com>
+---
+ drivers/gpu/drm/drm_ioctl.c | 4 ++++
+ include/uapi/drm/drm.h | 10 +++++++++-
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
+index f03ffbacfe9b48..7810fce89a446c 100644
+--- a/drivers/gpu/drm/drm_ioctl.c
++++ b/drivers/gpu/drm/drm_ioctl.c
+@@ -301,6 +301,10 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_
+ case DRM_CAP_CRTC_IN_VBLANK_EVENT:
+ req->value = 1;
+ break;
++ case DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP:
++ req->value = drm_core_check_feature(dev, DRIVER_ATOMIC) &&
++ dev->mode_config.async_page_flip;
++ break;
+ default:
+ return -EINVAL;
+ }
+diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
+index 794c1d857677d9..58baefe32c23c7 100644
+--- a/include/uapi/drm/drm.h
++++ b/include/uapi/drm/drm.h
+@@ -713,7 +713,8 @@ struct drm_gem_open {
+ /**
+ * DRM_CAP_ASYNC_PAGE_FLIP
+ *
+- * If set to 1, the driver supports &DRM_MODE_PAGE_FLIP_ASYNC.
++ * If set to 1, the driver supports &DRM_MODE_PAGE_FLIP_ASYNC for legacy
++ * page-flips.
+ */
+ #define DRM_CAP_ASYNC_PAGE_FLIP 0x7
+ /**
+@@ -773,6 +774,13 @@ struct drm_gem_open {
+ * :ref:`drm_sync_objects`.
+ */
+ #define DRM_CAP_SYNCOBJ_TIMELINE 0x14
++/**
++ * DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP
++ *
++ * If set to 1, the driver supports &DRM_MODE_PAGE_FLIP_ASYNC for atomic
++ * commits.
++ */
++#define DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP 0x15
+
+ /* DRM_IOCTL_GET_CAP ioctl argument type */
+ struct drm_get_cap {
+From 949c86212c122d35a463478f1aeb44cd52722e3c Mon Sep 17 00:00:00 2001
+From: Krunoslav Kovac <krunoslav.kovac@amd.com>
+Date: Tue, 8 Aug 2023 14:33:42 -0400
+Subject: [PATCH] drm/amd/display: PQ tail accuracy
+
+[WHY & HOW]
+HW LUTs changed slightly in DCN3: 256 base+slope pairs were replaced by
+257 bases. Code was still calculating all 256 base+slope and then
+creating 257th pt as last base + last slope.
+This was done in wrong format, and then "fixed" it by making the last
+two points the same thus making the last slope=0.
+However, this also created some precision problems near the end that
+are not visible but they do show up with capture cards.
+
+Solution is to calculate 257 and remove deltas since we no longer have
+those HW registers.
+
+Reviewed-by: Anthony Koo <anthony.koo@amd.com>
+Acked-by: Wayne Lin <wayne.lin@amd.com>
+Signed-off-by: Krunoslav Kovac <krunoslav.kovac@amd.com>
+Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit 4d5fd3d08ea9926fb2031ff7cfb4d72b7c950301)
+Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
+---
+ .../amd/display/dc/dcn30/dcn30_cm_common.c | 104 ++++++------------
+ .../gpu/drm/amd/display/dc/dcn30/dcn30_mpc.c | 17 ---
+ 2 files changed, 33 insertions(+), 88 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
+index e0df9b0065f9c0..ddb344056d4013 100644
+--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
++++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
+@@ -114,7 +114,6 @@ bool cm3_helper_translate_curve_to_hw_format(
+ struct pwl_result_data *rgb;
+ struct pwl_result_data *rgb_plus_1;
+ struct pwl_result_data *rgb_minus_1;
+- struct fixed31_32 end_value;
+
+ int32_t region_start, region_end;
+ int32_t i;
+@@ -176,7 +175,7 @@ bool cm3_helper_translate_curve_to_hw_format(
+ NUMBER_SW_SEGMENTS;
+ for (i = start_index; i < start_index + NUMBER_SW_SEGMENTS;
+ i += increment) {
+- if (j == hw_points - 1)
++ if (j == hw_points)
+ break;
+ rgb_resulted[j].red = output_tf->tf_pts.red[i];
+ rgb_resulted[j].green = output_tf->tf_pts.green[i];
+@@ -187,13 +186,13 @@ bool cm3_helper_translate_curve_to_hw_format(
+
+ /* last point */
+ start_index = (region_end + MAX_LOW_POINT) * NUMBER_SW_SEGMENTS;
+- rgb_resulted[hw_points - 1].red = output_tf->tf_pts.red[start_index];
+- rgb_resulted[hw_points - 1].green = output_tf->tf_pts.green[start_index];
+- rgb_resulted[hw_points - 1].blue = output_tf->tf_pts.blue[start_index];
++ rgb_resulted[hw_points].red = output_tf->tf_pts.red[start_index];
++ rgb_resulted[hw_points].green = output_tf->tf_pts.green[start_index];
++ rgb_resulted[hw_points].blue = output_tf->tf_pts.blue[start_index];
+
+- rgb_resulted[hw_points].red = rgb_resulted[hw_points - 1].red;
+- rgb_resulted[hw_points].green = rgb_resulted[hw_points - 1].green;
+- rgb_resulted[hw_points].blue = rgb_resulted[hw_points - 1].blue;
++ rgb_resulted[hw_points+1].red = rgb_resulted[hw_points].red;
++ rgb_resulted[hw_points+1].green = rgb_resulted[hw_points].green;
++ rgb_resulted[hw_points+1].blue = rgb_resulted[hw_points].blue;
+
+ // All 3 color channels have same x
+ corner_points[0].red.x = dc_fixpt_pow(dc_fixpt_from_int(2),
+@@ -220,34 +219,16 @@ bool cm3_helper_translate_curve_to_hw_format(
+ /* see comment above, m_arrPoints[1].y should be the Y value for the
+ * region end (m_numOfHwPoints), not last HW point(m_numOfHwPoints - 1)
+ */
+- corner_points[1].red.y = rgb_resulted[hw_points - 1].red;
+- corner_points[1].green.y = rgb_resulted[hw_points - 1].green;
+- corner_points[1].blue.y = rgb_resulted[hw_points - 1].blue;
++ corner_points[1].red.y = rgb_resulted[hw_points].red;
++ corner_points[1].green.y = rgb_resulted[hw_points].green;
++ corner_points[1].blue.y = rgb_resulted[hw_points].blue;
+ corner_points[1].red.slope = dc_fixpt_zero;
+ corner_points[1].green.slope = dc_fixpt_zero;
+ corner_points[1].blue.slope = dc_fixpt_zero;
+
+- if (output_tf->tf == TRANSFER_FUNCTION_PQ || output_tf->tf == TRANSFER_FUNCTION_HLG) {
+- /* for PQ/HLG, we want to have a straight line from last HW X point,
+- * and the slope to be such that we hit 1.0 at 10000/1000 nits.
+- */
+-
+- if (output_tf->tf == TRANSFER_FUNCTION_PQ)
+- end_value = dc_fixpt_from_int(125);
+- else
+- end_value = dc_fixpt_from_fraction(125, 10);
+-
+- corner_points[1].red.slope = dc_fixpt_div(
+- dc_fixpt_sub(dc_fixpt_one, corner_points[1].red.y),
+- dc_fixpt_sub(end_value, corner_points[1].red.x));
+- corner_points[1].green.slope = dc_fixpt_div(
+- dc_fixpt_sub(dc_fixpt_one, corner_points[1].green.y),
+- dc_fixpt_sub(end_value, corner_points[1].green.x));
+- corner_points[1].blue.slope = dc_fixpt_div(
+- dc_fixpt_sub(dc_fixpt_one, corner_points[1].blue.y),
+- dc_fixpt_sub(end_value, corner_points[1].blue.x));
+- }
+- lut_params->hw_points_num = hw_points;
++ // DCN3+ have 257 pts in lieu of no separate slope registers
++ // Prior HW had 256 base+slope pairs
++ lut_params->hw_points_num = hw_points + 1;
+
+ k = 0;
+ for (i = 1; i < MAX_REGIONS_NUMBER; i++) {
+@@ -267,38 +248,37 @@ bool cm3_helper_translate_curve_to_hw_format(
+ rgb_plus_1 = rgb_resulted + 1;
+ rgb_minus_1 = rgb;
+
+- i = 1;
+- while (i != hw_points + 1) {
+- if (i >= hw_points - 1) {
+- if (dc_fixpt_lt(rgb_plus_1->red, rgb->red))
+- rgb_plus_1->red = dc_fixpt_add(rgb->red, rgb_minus_1->delta_red);
+- if (dc_fixpt_lt(rgb_plus_1->green, rgb->green))
+- rgb_plus_1->green = dc_fixpt_add(rgb->green, rgb_minus_1->delta_green);
+- if (dc_fixpt_lt(rgb_plus_1->blue, rgb->blue))
+- rgb_plus_1->blue = dc_fixpt_add(rgb->blue, rgb_minus_1->delta_blue);
+- }
+-
+- rgb->delta_red = dc_fixpt_sub(rgb_plus_1->red, rgb->red);
+- rgb->delta_green = dc_fixpt_sub(rgb_plus_1->green, rgb->green);
+- rgb->delta_blue = dc_fixpt_sub(rgb_plus_1->blue, rgb->blue);
++ if (fixpoint == true) {
++ i = 1;
++ while (i != hw_points + 2) {
++ if (i >= hw_points) {
++ if (dc_fixpt_lt(rgb_plus_1->red, rgb->red))
++ rgb_plus_1->red = dc_fixpt_add(rgb->red,
++ rgb_minus_1->delta_red);
++ if (dc_fixpt_lt(rgb_plus_1->green, rgb->green))
++ rgb_plus_1->green = dc_fixpt_add(rgb->green,
++ rgb_minus_1->delta_green);
++ if (dc_fixpt_lt(rgb_plus_1->blue, rgb->blue))
++ rgb_plus_1->blue = dc_fixpt_add(rgb->blue,
++ rgb_minus_1->delta_blue);
++ }
+
+- if (fixpoint == true) {
+ rgb->delta_red_reg = dc_fixpt_clamp_u0d10(rgb->delta_red);
+ rgb->delta_green_reg = dc_fixpt_clamp_u0d10(rgb->delta_green);
+ rgb->delta_blue_reg = dc_fixpt_clamp_u0d10(rgb->delta_blue);
+ rgb->red_reg = dc_fixpt_clamp_u0d14(rgb->red);
+ rgb->green_reg = dc_fixpt_clamp_u0d14(rgb->green);
+ rgb->blue_reg = dc_fixpt_clamp_u0d14(rgb->blue);
+- }
+
+- ++rgb_plus_1;
+- rgb_minus_1 = rgb;
+- ++rgb;
+- ++i;
++ ++rgb_plus_1;
++ rgb_minus_1 = rgb;
++ ++rgb;
++ ++i;
++ }
+ }
+ cm3_helper_convert_to_custom_float(rgb_resulted,
+ lut_params->corner_points,
+- hw_points, fixpoint);
++ hw_points+1, fixpoint);
+
+ return true;
+ }
+@@ -603,24 +583,6 @@ bool cm3_helper_convert_to_custom_float(
+ return false;
+ }
+
+- if (!convert_to_custom_float_format(rgb->delta_red, &fmt,
+- &rgb->delta_red_reg)) {
+- BREAK_TO_DEBUGGER();
+- return false;
+- }
+-
+- if (!convert_to_custom_float_format(rgb->delta_green, &fmt,
+- &rgb->delta_green_reg)) {
+- BREAK_TO_DEBUGGER();
+- return false;
+- }
+-
+- if (!convert_to_custom_float_format(rgb->delta_blue, &fmt,
+- &rgb->delta_blue_reg)) {
+- BREAK_TO_DEBUGGER();
+- return false;
+- }
+-
+ ++rgb;
+ ++i;
+ }
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mpc.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mpc.c
+index 6cf40c1332bc37..d1500b2238580b 100644
+--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mpc.c
++++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_mpc.c
+@@ -278,22 +278,10 @@ static void mpc3_program_ogam_pwl(
+ {
+ uint32_t i;
+ struct dcn30_mpc *mpc30 = TO_DCN30_MPC(mpc);
+- uint32_t last_base_value_red = rgb[num-1].red_reg + rgb[num-1].delta_red_reg;
+- uint32_t last_base_value_green = rgb[num-1].green_reg + rgb[num-1].delta_green_reg;
+- uint32_t last_base_value_blue = rgb[num-1].blue_reg + rgb[num-1].delta_blue_reg;
+-
+- /*the entries of DCN3AG gamma LUTs take 18bit base values as opposed to
+- *38 base+delta values per entry in earlier DCN architectures
+- *last base value for our lut is compute by adding the last base value
+- *in our data + last delta
+- */
+
+ if (is_rgb_equal(rgb, num)) {
+ for (i = 0 ; i < num; i++)
+ REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, rgb[i].red_reg);
+-
+- REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, last_base_value_red);
+-
+ } else {
+
+ REG_UPDATE(MPCC_OGAM_LUT_CONTROL[mpcc_id],
+@@ -302,8 +290,6 @@ static void mpc3_program_ogam_pwl(
+ for (i = 0 ; i < num; i++)
+ REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, rgb[i].red_reg);
+
+- REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, last_base_value_red);
+-
+ REG_SET(MPCC_OGAM_LUT_INDEX[mpcc_id], 0, MPCC_OGAM_LUT_INDEX, 0);
+
+ REG_UPDATE(MPCC_OGAM_LUT_CONTROL[mpcc_id],
+@@ -312,8 +298,6 @@ static void mpc3_program_ogam_pwl(
+ for (i = 0 ; i < num; i++)
+ REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, rgb[i].green_reg);
+
+- REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, last_base_value_green);
+-
+ REG_SET(MPCC_OGAM_LUT_INDEX[mpcc_id], 0, MPCC_OGAM_LUT_INDEX, 0);
+
+ REG_UPDATE(MPCC_OGAM_LUT_CONTROL[mpcc_id],
+@@ -322,7 +306,6 @@ static void mpc3_program_ogam_pwl(
+ for (i = 0 ; i < num; i++)
+ REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, rgb[i].blue_reg);
+
+- REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, last_base_value_blue);
+ }
+
+ }
+From 1edf3fbbeb36440e1222c2fe0e8127fb804c5278 Mon Sep 17 00:00:00 2001
+From: Hamza Mahfooz <hamza.mahfooz@amd.com>
+Date: Fri, 4 Aug 2023 11:13:04 -0400
+Subject: [PATCH] drm/amd/display: ensure async flips are only accepted for
+ fast updates
+
+We should be checking to see if async flips are supported in
+amdgpu_dm_atomic_check() (i.e. not dm_crtc_helper_atomic_check()). Also,
+async flipping isn't supported if a plane's framebuffer changes memory
+domains during an atomic commit. So, move the check from
+dm_crtc_helper_atomic_check() to amdgpu_dm_atomic_check() and check if
+the memory domain has changed in amdgpu_dm_atomic_check().
+
+Cc: stable@vger.kernel.org
+Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2733
+Fixes: c1e18c44dc7f ("drm/amd/display: only accept async flips for fast updates")
+Reviewed-by: Harry Wentland <harry.wentland@amd.com>
+Signed-off-by: Hamza Mahfooz <hamza.mahfooz@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit a7c0cad0dc060bb77e9c9d235d68441b0fc69507)
+Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
+---
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 12 ------------
+ 1 file changed, 12 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+index be1ebe826442a4..4b223db0cf2fe8 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+@@ -473,18 +473,6 @@ static int dm_crtc_helper_atomic_check(struct drm_crtc *crtc,
+ return -EINVAL;
+ }
+
+- /*
+- * Only allow async flips for fast updates that don't change the FB
+- * pitch, the DCC state, rotation, etc.
+- */
+- if (crtc_state->async_flip &&
+- dm_crtc_state->update_type != UPDATE_TYPE_FAST) {
+- drm_dbg_atomic(crtc->dev,
+- "[CRTC:%d:%s] async flips are only supported for fast updates\n",
+- crtc->base.id, crtc->name);
+- return -EINVAL;
+- }
+-
+ /* In some use cases, like reset, no stream is attached */
+ if (!dm_crtc_state->stream)
+ return 0;
diff --git a/SOURCES/kernel-aarch64-16k-debug-fedora.config b/SOURCES/kernel-aarch64-16k-debug-fedora.config
index 91c9e52..f0d8660 100644
--- a/SOURCES/kernel-aarch64-16k-debug-fedora.config
+++ b/SOURCES/kernel-aarch64-16k-debug-fedora.config
@@ -9794,10 +9794,9 @@ CONFIG_ZYNQMP_PM_DOMAINS=y
CONFIG_ZYNQMP_POWER=y
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-16k-fedora.config b/SOURCES/kernel-aarch64-16k-fedora.config
index 7fd991c..26b3744 100644
--- a/SOURCES/kernel-aarch64-16k-fedora.config
+++ b/SOURCES/kernel-aarch64-16k-fedora.config
@@ -9765,10 +9765,9 @@ CONFIG_ZYNQMP_PM_DOMAINS=y
CONFIG_ZYNQMP_POWER=y
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-64k-debug-rhel.config b/SOURCES/kernel-aarch64-64k-debug-rhel.config
index 8d8728a..90273ac 100644
--- a/SOURCES/kernel-aarch64-64k-debug-rhel.config
+++ b/SOURCES/kernel-aarch64-64k-debug-rhel.config
@@ -7939,10 +7939,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-64k-rhel.config b/SOURCES/kernel-aarch64-64k-rhel.config
index 04080a5..52d7803 100644
--- a/SOURCES/kernel-aarch64-64k-rhel.config
+++ b/SOURCES/kernel-aarch64-64k-rhel.config
@@ -7914,10 +7914,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-debug-fedora.config b/SOURCES/kernel-aarch64-debug-fedora.config
index 1df297b..7615479 100644
--- a/SOURCES/kernel-aarch64-debug-fedora.config
+++ b/SOURCES/kernel-aarch64-debug-fedora.config
@@ -9794,10 +9794,9 @@ CONFIG_ZYNQMP_PM_DOMAINS=y
CONFIG_ZYNQMP_POWER=y
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-debug-rhel.config b/SOURCES/kernel-aarch64-debug-rhel.config
index cf09aa9..9b874db 100644
--- a/SOURCES/kernel-aarch64-debug-rhel.config
+++ b/SOURCES/kernel-aarch64-debug-rhel.config
@@ -7935,10 +7935,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-fedora.config b/SOURCES/kernel-aarch64-fedora.config
index ca1c749..c5fd773 100644
--- a/SOURCES/kernel-aarch64-fedora.config
+++ b/SOURCES/kernel-aarch64-fedora.config
@@ -9765,10 +9765,9 @@ CONFIG_ZYNQMP_PM_DOMAINS=y
CONFIG_ZYNQMP_POWER=y
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-rhel.config b/SOURCES/kernel-aarch64-rhel.config
index 5a5c2b1..6645911 100644
--- a/SOURCES/kernel-aarch64-rhel.config
+++ b/SOURCES/kernel-aarch64-rhel.config
@@ -7910,10 +7910,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-rt-debug-rhel.config b/SOURCES/kernel-aarch64-rt-debug-rhel.config
index b214f20..fb5a0f3 100644
--- a/SOURCES/kernel-aarch64-rt-debug-rhel.config
+++ b/SOURCES/kernel-aarch64-rt-debug-rhel.config
@@ -7996,10 +7996,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-aarch64-rt-rhel.config b/SOURCES/kernel-aarch64-rt-rhel.config
index 75ba30a..618b209 100644
--- a/SOURCES/kernel-aarch64-rt-rhel.config
+++ b/SOURCES/kernel-aarch64-rt-rhel.config
@@ -7971,10 +7971,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-ppc64le-debug-fedora.config b/SOURCES/kernel-ppc64le-debug-fedora.config
index f9fd408..651a070 100644
--- a/SOURCES/kernel-ppc64le-debug-fedora.config
+++ b/SOURCES/kernel-ppc64le-debug-fedora.config
@@ -8244,10 +8244,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-ppc64le-debug-rhel.config b/SOURCES/kernel-ppc64le-debug-rhel.config
index 2f56c33..878cd33 100644
--- a/SOURCES/kernel-ppc64le-debug-rhel.config
+++ b/SOURCES/kernel-ppc64le-debug-rhel.config
@@ -7414,10 +7414,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-ppc64le-fedora.config b/SOURCES/kernel-ppc64le-fedora.config
index 5ed305c..cf58241 100644
--- a/SOURCES/kernel-ppc64le-fedora.config
+++ b/SOURCES/kernel-ppc64le-fedora.config
@@ -8213,10 +8213,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-ppc64le-rhel.config b/SOURCES/kernel-ppc64le-rhel.config
index 8409878..95d5ee5 100644
--- a/SOURCES/kernel-ppc64le-rhel.config
+++ b/SOURCES/kernel-ppc64le-rhel.config
@@ -7391,10 +7391,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-s390x-debug-fedora.config b/SOURCES/kernel-s390x-debug-fedora.config
index c7b92d3..63c8715 100644
--- a/SOURCES/kernel-s390x-debug-fedora.config
+++ b/SOURCES/kernel-s390x-debug-fedora.config
@@ -8181,10 +8181,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-s390x-debug-rhel.config b/SOURCES/kernel-s390x-debug-rhel.config
index 56012a1..c076c30 100644
--- a/SOURCES/kernel-s390x-debug-rhel.config
+++ b/SOURCES/kernel-s390x-debug-rhel.config
@@ -7400,10 +7400,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-s390x-fedora.config b/SOURCES/kernel-s390x-fedora.config
index 5fb78a9..7bd03d7 100644
--- a/SOURCES/kernel-s390x-fedora.config
+++ b/SOURCES/kernel-s390x-fedora.config
@@ -8150,10 +8150,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-s390x-rhel.config b/SOURCES/kernel-s390x-rhel.config
index 2782394..1740360 100644
--- a/SOURCES/kernel-s390x-rhel.config
+++ b/SOURCES/kernel-s390x-rhel.config
@@ -7377,10 +7377,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-s390x-zfcpdump-rhel.config b/SOURCES/kernel-s390x-zfcpdump-rhel.config
index a3cb80d..9063e22 100644
--- a/SOURCES/kernel-s390x-zfcpdump-rhel.config
+++ b/SOURCES/kernel-s390x-zfcpdump-rhel.config
@@ -7400,10 +7400,9 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
-CONFIG_LEGION_LAPTOP=m
-CONFIG_WINESYNC=y
diff --git a/SOURCES/kernel-x86_64-debug-fedora.config b/SOURCES/kernel-x86_64-debug-fedora.config
index 6eab1bd..cf392af 100644
--- a/SOURCES/kernel-x86_64-debug-fedora.config
+++ b/SOURCES/kernel-x86_64-debug-fedora.config
@@ -6705,7 +6705,7 @@ CONFIG_SND_SOC_AK5558=m
CONFIG_SND_SOC_AMD_ACP3x=m
CONFIG_SND_SOC_AMD_ACP5x=m
CONFIG_SND_SOC_AMD_ACP6x=m
-# CONFIG_SND_SOC_AMD_ACP_COMMON is not set
+CONFIG_SND_SOC_AMD_ACP_COMMON=m
CONFIG_SND_SOC_AMD_ACP=m
CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH=m
CONFIG_SND_SOC_AMD_CZ_RT5645_MACH=m
@@ -7122,7 +7122,7 @@ CONFIG_SND_SOC_WSA884X=m
# CONFIG_SND_SOC_XTFPGA_I2S is not set
CONFIG_SND_SOC_ZL38060=m
CONFIG_SND_SONICVIBES=m
-# CONFIG_SND_SPI is not set
+CONFIG_SND_SPI=y
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI=m
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM=m
# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
@@ -8805,12 +8805,14 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
CONFIG_LEGION_LAPTOP=m
+CONFIG_ACPI_CALL=m
CONFIG_MFD_STEAMDECK=m
CONFIG_SENSORS_STEAMDECK=m
CONFIG_LEDS_STEAMDECK=m
@@ -8844,4 +8846,16 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
# CONFIG_USB_DUMMY_HCD is not set
# CONFIG_USB_CONFIGFS is not set
# CONFIG_PHY_SAMSUNG_USB2 is not set
-CONFIG_WINESYNC=y
+CONFIG_SND_SOC_AMD_SOF_MACH=m
+CONFIG_SND_SOC_AMD_MACH_COMMON=m
+CONFIG_SND_SOC_SOF=m
+CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y
+CONFIG_SND_SOC_SOF_IPC3=y
+CONFIG_SND_SOC_SOF_INTEL_IPC4=y
+CONFIG_SND_SOC_SOF_AMD_COMMON=m
+CONFIG_SND_SOC_SOF_AMD_ACP63=m
+# CONFIG_SND_SOC_AMD_ACP_PCI is not set
+# CONFIG_SND_AMD_ASOC_RENOIR is not set
+# CONFIG_SND_AMD_ASOC_REMBRANDT is not set
+# CONFIG_SND_SOC_AMD_LEGACY_MACH is not set
+CONFIG_SND_SOC_TOPOLOGY=y
diff --git a/SOURCES/kernel-x86_64-debug-rhel.config b/SOURCES/kernel-x86_64-debug-rhel.config
index 7807bc9..6bcbd0d 100644
--- a/SOURCES/kernel-x86_64-debug-rhel.config
+++ b/SOURCES/kernel-x86_64-debug-rhel.config
@@ -5907,7 +5907,7 @@ CONFIG_SND_SEQ_UMP=y
# CONFIG_SND_SOC_AMD_ACP3x is not set
# CONFIG_SND_SOC_AMD_ACP5x is not set
CONFIG_SND_SOC_AMD_ACP6x=m
-# CONFIG_SND_SOC_AMD_ACP_COMMON is not set
+CONFIG_SND_SOC_AMD_ACP_COMMON=m
# CONFIG_SND_SOC_AMD_ACP is not set
# CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH is not set
# CONFIG_SND_SOC_AMD_CZ_RT5645_MACH is not set
@@ -6316,7 +6316,7 @@ CONFIG_SND_SOC_WM8804=m
# CONFIG_SND_SOC_XTFPGA_I2S is not set
# CONFIG_SND_SOC_ZL38060 is not set
# CONFIG_SND_SONICVIBES is not set
-# CONFIG_SND_SPI is not set
+CONFIG_SND_SPI=y
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI=m
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM=m
# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
@@ -7749,12 +7749,14 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
CONFIG_LEGION_LAPTOP=m
+CONFIG_ACPI_CALL=m
CONFIG_MFD_STEAMDECK=m
CONFIG_SENSORS_STEAMDECK=m
CONFIG_LEDS_STEAMDECK=m
@@ -7793,4 +7795,16 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
# CONFIG_USB_DUMMY_HCD is not set
# CONFIG_USB_CONFIGFS is not set
# CONFIG_PHY_SAMSUNG_USB2 is not set
-CONFIG_WINESYNC=y
+CONFIG_SND_SOC_AMD_SOF_MACH=m
+CONFIG_SND_SOC_AMD_MACH_COMMON=m
+CONFIG_SND_SOC_SOF=m
+CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y
+CONFIG_SND_SOC_SOF_IPC3=y
+CONFIG_SND_SOC_SOF_INTEL_IPC4=y
+CONFIG_SND_SOC_SOF_AMD_COMMON=m
+CONFIG_SND_SOC_SOF_AMD_ACP63=m
+# CONFIG_SND_SOC_AMD_ACP_PCI is not set
+# CONFIG_SND_AMD_ASOC_RENOIR is not set
+# CONFIG_SND_AMD_ASOC_REMBRANDT is not set
+# CONFIG_SND_SOC_AMD_LEGACY_MACH is not set
+CONFIG_SND_SOC_TOPOLOGY=y
diff --git a/SOURCES/kernel-x86_64-fedora.config b/SOURCES/kernel-x86_64-fedora.config
index 065fece..6d9d376 100644
--- a/SOURCES/kernel-x86_64-fedora.config
+++ b/SOURCES/kernel-x86_64-fedora.config
@@ -6676,7 +6676,7 @@ CONFIG_SND_SOC_AK5558=m
CONFIG_SND_SOC_AMD_ACP3x=m
CONFIG_SND_SOC_AMD_ACP5x=m
CONFIG_SND_SOC_AMD_ACP6x=m
-# CONFIG_SND_SOC_AMD_ACP_COMMON is not set
+CONFIG_SND_SOC_AMD_ACP_COMMON=m
CONFIG_SND_SOC_AMD_ACP=m
CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH=m
CONFIG_SND_SOC_AMD_CZ_RT5645_MACH=m
@@ -7092,7 +7092,7 @@ CONFIG_SND_SOC_WSA884X=m
# CONFIG_SND_SOC_XTFPGA_I2S is not set
CONFIG_SND_SOC_ZL38060=m
CONFIG_SND_SONICVIBES=m
-# CONFIG_SND_SPI is not set
+CONFIG_SND_SPI=y
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI=m
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM=m
# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
@@ -8775,12 +8775,14 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
CONFIG_LEGION_LAPTOP=m
+CONFIG_ACPI_CALL=m
CONFIG_MFD_STEAMDECK=m
CONFIG_SENSORS_STEAMDECK=m
CONFIG_LEDS_STEAMDECK=m
@@ -8814,4 +8816,16 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
# CONFIG_USB_DUMMY_HCD is not set
# CONFIG_USB_CONFIGFS is not set
# CONFIG_PHY_SAMSUNG_USB2 is not set
-CONFIG_WINESYNC=y
+CONFIG_SND_SOC_AMD_SOF_MACH=m
+CONFIG_SND_SOC_AMD_MACH_COMMON=m
+CONFIG_SND_SOC_SOF=m
+CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y
+CONFIG_SND_SOC_SOF_IPC3=y
+CONFIG_SND_SOC_SOF_INTEL_IPC4=y
+CONFIG_SND_SOC_SOF_AMD_COMMON=m
+CONFIG_SND_SOC_SOF_AMD_ACP63=m
+# CONFIG_SND_SOC_AMD_ACP_PCI is not set
+# CONFIG_SND_AMD_ASOC_RENOIR is not set
+# CONFIG_SND_AMD_ASOC_REMBRANDT is not set
+# CONFIG_SND_SOC_AMD_LEGACY_MACH is not set
+CONFIG_SND_SOC_TOPOLOGY=y
diff --git a/SOURCES/kernel-x86_64-rhel.config b/SOURCES/kernel-x86_64-rhel.config
index 9115001..95ebb31 100644
--- a/SOURCES/kernel-x86_64-rhel.config
+++ b/SOURCES/kernel-x86_64-rhel.config
@@ -5884,7 +5884,7 @@ CONFIG_SND_SEQ_UMP=y
# CONFIG_SND_SOC_AMD_ACP3x is not set
# CONFIG_SND_SOC_AMD_ACP5x is not set
CONFIG_SND_SOC_AMD_ACP6x=m
-# CONFIG_SND_SOC_AMD_ACP_COMMON is not set
+CONFIG_SND_SOC_AMD_ACP_COMMON=m
# CONFIG_SND_SOC_AMD_ACP is not set
# CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH is not set
# CONFIG_SND_SOC_AMD_CZ_RT5645_MACH is not set
@@ -6292,7 +6292,7 @@ CONFIG_SND_SOC_WM8804=m
# CONFIG_SND_SOC_XTFPGA_I2S is not set
# CONFIG_SND_SOC_ZL38060 is not set
# CONFIG_SND_SONICVIBES is not set
-# CONFIG_SND_SPI is not set
+CONFIG_SND_SPI=y
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI=m
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM=m
# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
@@ -7725,12 +7725,14 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
CONFIG_LEGION_LAPTOP=m
+CONFIG_ACPI_CALL=m
CONFIG_MFD_STEAMDECK=m
CONFIG_SENSORS_STEAMDECK=m
CONFIG_LEDS_STEAMDECK=m
@@ -7769,4 +7771,16 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
# CONFIG_USB_DUMMY_HCD is not set
# CONFIG_USB_CONFIGFS is not set
# CONFIG_PHY_SAMSUNG_USB2 is not set
-CONFIG_WINESYNC=y
+CONFIG_SND_SOC_AMD_SOF_MACH=m
+CONFIG_SND_SOC_AMD_MACH_COMMON=m
+CONFIG_SND_SOC_SOF=m
+CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y
+CONFIG_SND_SOC_SOF_IPC3=y
+CONFIG_SND_SOC_SOF_INTEL_IPC4=y
+CONFIG_SND_SOC_SOF_AMD_COMMON=m
+CONFIG_SND_SOC_SOF_AMD_ACP63=m
+# CONFIG_SND_SOC_AMD_ACP_PCI is not set
+# CONFIG_SND_AMD_ASOC_RENOIR is not set
+# CONFIG_SND_AMD_ASOC_REMBRANDT is not set
+# CONFIG_SND_SOC_AMD_LEGACY_MACH is not set
+CONFIG_SND_SOC_TOPOLOGY=y
diff --git a/SOURCES/kernel-x86_64-rt-debug-rhel.config b/SOURCES/kernel-x86_64-rt-debug-rhel.config
index a9ec149..6ad9338 100644
--- a/SOURCES/kernel-x86_64-rt-debug-rhel.config
+++ b/SOURCES/kernel-x86_64-rt-debug-rhel.config
@@ -5961,7 +5961,7 @@ CONFIG_SND_SEQ_UMP=y
# CONFIG_SND_SOC_AMD_ACP3x is not set
# CONFIG_SND_SOC_AMD_ACP5x is not set
CONFIG_SND_SOC_AMD_ACP6x=m
-# CONFIG_SND_SOC_AMD_ACP_COMMON is not set
+CONFIG_SND_SOC_AMD_ACP_COMMON=m
# CONFIG_SND_SOC_AMD_ACP is not set
# CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH is not set
# CONFIG_SND_SOC_AMD_CZ_RT5645_MACH is not set
@@ -6370,7 +6370,7 @@ CONFIG_SND_SOC_WM8804=m
# CONFIG_SND_SOC_XTFPGA_I2S is not set
# CONFIG_SND_SOC_ZL38060 is not set
# CONFIG_SND_SONICVIBES is not set
-# CONFIG_SND_SPI is not set
+CONFIG_SND_SPI=y
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI=m
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM=m
# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
@@ -7811,12 +7811,14 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
CONFIG_LEGION_LAPTOP=m
+CONFIG_ACPI_CALL=m
CONFIG_MFD_STEAMDECK=m
CONFIG_SENSORS_STEAMDECK=m
CONFIG_LEDS_STEAMDECK=m
@@ -7855,4 +7857,16 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
# CONFIG_USB_DUMMY_HCD is not set
# CONFIG_USB_CONFIGFS is not set
# CONFIG_PHY_SAMSUNG_USB2 is not set
-CONFIG_WINESYNC=y
+CONFIG_SND_SOC_AMD_SOF_MACH=m
+CONFIG_SND_SOC_AMD_MACH_COMMON=m
+CONFIG_SND_SOC_SOF=m
+CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y
+CONFIG_SND_SOC_SOF_IPC3=y
+CONFIG_SND_SOC_SOF_INTEL_IPC4=y
+CONFIG_SND_SOC_SOF_AMD_COMMON=m
+CONFIG_SND_SOC_SOF_AMD_ACP63=m
+# CONFIG_SND_SOC_AMD_ACP_PCI is not set
+# CONFIG_SND_AMD_ASOC_RENOIR is not set
+# CONFIG_SND_AMD_ASOC_REMBRANDT is not set
+# CONFIG_SND_SOC_AMD_LEGACY_MACH is not set
+CONFIG_SND_SOC_TOPOLOGY=y
diff --git a/SOURCES/kernel-x86_64-rt-rhel.config b/SOURCES/kernel-x86_64-rt-rhel.config
index 741d909..a83132e 100644
--- a/SOURCES/kernel-x86_64-rt-rhel.config
+++ b/SOURCES/kernel-x86_64-rt-rhel.config
@@ -5938,7 +5938,7 @@ CONFIG_SND_SEQ_UMP=y
# CONFIG_SND_SOC_AMD_ACP3x is not set
# CONFIG_SND_SOC_AMD_ACP5x is not set
CONFIG_SND_SOC_AMD_ACP6x=m
-# CONFIG_SND_SOC_AMD_ACP_COMMON is not set
+CONFIG_SND_SOC_AMD_ACP_COMMON=m
# CONFIG_SND_SOC_AMD_ACP is not set
# CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH is not set
# CONFIG_SND_SOC_AMD_CZ_RT5645_MACH is not set
@@ -6346,7 +6346,7 @@ CONFIG_SND_SOC_WM8804=m
# CONFIG_SND_SOC_XTFPGA_I2S is not set
# CONFIG_SND_SOC_ZL38060 is not set
# CONFIG_SND_SONICVIBES is not set
-# CONFIG_SND_SPI is not set
+CONFIG_SND_SPI=y
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI=m
CONFIG_SND_SST_ATOM_HIFI2_PLATFORM=m
# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
@@ -7787,12 +7787,14 @@ CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_I2C_NCT6775=m
CONFIG_ZENIFY=y
+CONFIG_WINESYNC=y
CONFIG_HID_IPTS=m
CONFIG_HID_ITHC=m
CONFIG_SURFACE_BOOK1_DGPU_SWITCH=m
CONFIG_IPC_CLASSES=y
CONFIG_LEDS_TPS68470=m
CONFIG_LEGION_LAPTOP=m
+CONFIG_ACPI_CALL=m
CONFIG_MFD_STEAMDECK=m
CONFIG_SENSORS_STEAMDECK=m
CONFIG_LEDS_STEAMDECK=m
@@ -7831,4 +7833,16 @@ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
# CONFIG_USB_DUMMY_HCD is not set
# CONFIG_USB_CONFIGFS is not set
# CONFIG_PHY_SAMSUNG_USB2 is not set
-CONFIG_WINESYNC=y
+CONFIG_SND_SOC_AMD_SOF_MACH=m
+CONFIG_SND_SOC_AMD_MACH_COMMON=m
+CONFIG_SND_SOC_SOF=m
+CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y
+CONFIG_SND_SOC_SOF_IPC3=y
+CONFIG_SND_SOC_SOF_INTEL_IPC4=y
+CONFIG_SND_SOC_SOF_AMD_COMMON=m
+CONFIG_SND_SOC_SOF_AMD_ACP63=m
+# CONFIG_SND_SOC_AMD_ACP_PCI is not set
+# CONFIG_SND_AMD_ASOC_RENOIR is not set
+# CONFIG_SND_AMD_ASOC_REMBRANDT is not set
+# CONFIG_SND_SOC_AMD_LEGACY_MACH is not set
+CONFIG_SND_SOC_TOPOLOGY=y
diff --git a/SOURCES/linux-surface.patch b/SOURCES/linux-surface.patch
index 68a1525..3378feb 100644
--- a/SOURCES/linux-surface.patch
+++ b/SOURCES/linux-surface.patch
@@ -8074,14 +8074,13 @@ index 51ec9e7e784f0..40554890d7211 100644
if (drv && drv->shutdown)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
-index e3e915329510f..666ff1e9b6d7b 100644
+index ae95d0950..7a6d76c41 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
-@@ -6190,3 +6190,39 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a31, dpc_log_size);
- DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5020, of_pci_make_dev_node);
+@@ -6212,6 +6212,42 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5020, of_pci_make_dev_node);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5021, of_pci_make_dev_node);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_REDHAT, 0x0005, of_pci_make_dev_node);
-+
+
+static const struct dmi_system_id no_shutdown_dmi_table[] = {
+ /*
+ * Systems on which some devices should not be touched during shutdown.
@@ -8117,6 +8116,11 @@ index e3e915329510f..666ff1e9b6d7b 100644
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x462f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown); // Thunderbolt 4 NHI
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown); // GPU
++
+ /*
+ * Devices known to require a longer delay before first config space access
+ * after reset recovery or resume from D3cold:
+
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 8c7c2c3c6c652..0c223b04dff91 100644
--- a/include/linux/pci.h
diff --git a/SOURCES/patch-6.6-redhat.patch b/SOURCES/patch-6.6-redhat.patch
index 50b6ea9..7889222 100644
--- a/SOURCES/patch-6.6-redhat.patch
+++ b/SOURCES/patch-6.6-redhat.patch
@@ -41,7 +41,7 @@
40 files changed, 696 insertions(+), 182 deletions(-)
diff --git a/Makefile b/Makefile
-index 03c52108af62..66048c4637d1 100644
+index 8ecebeb5642c..d2ae44a64210 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,18 @@ $(if $(filter __%, $(MAKECMDGOALS)), \
@@ -1004,7 +1004,7 @@ index c146378c7d03..f9e8d35eaccc 100644
* iommu_setup_default_domain - Set the default_domain for the group
* @group: Group to change
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
-index e3e915329510..929ca9fa61a7 100644
+index ae95d0950772..459f2b50d422 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -4410,6 +4410,30 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9000,
@@ -1200,7 +1200,7 @@ index 80b21d1c6eaf..b66c0683f2fc 100644
enum efi_secureboot_mode efi_get_secureboot_mode(efi_get_variable_t *get_var)
{
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
-index ac962c4cb44b..d0cedef6859c 100644
+index 2b8d85aae083..6a560e1abb59 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -405,6 +405,8 @@ LSM_HOOK(void, LSM_RET_VOID, bpf_prog_free_security, struct bpf_prog_aux *aux)
diff --git a/SOURCES/rog-ally-alsa.patch b/SOURCES/rog-ally-alsa.patch
deleted file mode 100644
index 189b049..0000000
--- a/SOURCES/rog-ally-alsa.patch
+++ /dev/null
@@ -1,374 +0,0 @@
-From: Cristian Ciocaltea @ 2023-09-07 17:10 UTC (permalink / raw)
- To: James Schulman, David Rhodes, Richard Fitzgerald,
- Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
- Stefan Binding, Charles Keepax, Vitaly Rodionov
- Cc: alsa-devel, patches, linux-kernel, kernel
-
-Use dev_err_probe() helper where possible, to simplify error handling
-during probe.
-
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Acked-by: Charles Keepax <ckeepax@opensource.cirrus.com>
----
- sound/soc/codecs/cs35l41-i2c.c | 9 +++------
- sound/soc/codecs/cs35l41-spi.c | 9 +++------
- sound/soc/codecs/cs35l41.c | 34 ++++++++++++++++------------------
- 3 files changed, 22 insertions(+), 30 deletions(-)
-
-diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
-index 7ea890d7d387..9109203a7f25 100644
---- a/sound/soc/codecs/cs35l41-i2c.c
-+++ b/sound/soc/codecs/cs35l41-i2c.c
-@@ -35,7 +35,6 @@ static int cs35l41_i2c_probe(struct i2c_client *client)
- struct device *dev = &client->dev;
- struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(dev);
- const struct regmap_config *regmap_config = &cs35l41_regmap_i2c;
-- int ret;
-
- cs35l41 = devm_kzalloc(dev, sizeof(struct cs35l41_private), GFP_KERNEL);
-
-@@ -47,11 +46,9 @@ static int cs35l41_i2c_probe(struct i2c_client *client)
-
- i2c_set_clientdata(client, cs35l41);
- cs35l41->regmap = devm_regmap_init_i2c(client, regmap_config);
-- if (IS_ERR(cs35l41->regmap)) {
-- ret = PTR_ERR(cs35l41->regmap);
-- dev_err(cs35l41->dev, "Failed to allocate register map: %d\n", ret);
-- return ret;
-- }
-+ if (IS_ERR(cs35l41->regmap))
-+ return dev_err_probe(cs35l41->dev, PTR_ERR(cs35l41->regmap),
-+ "Failed to allocate register map\n");
-
- return cs35l41_probe(cs35l41, hw_cfg);
- }
-diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
-index 5c8bb24909eb..28e9c9473e60 100644
---- a/sound/soc/codecs/cs35l41-spi.c
-+++ b/sound/soc/codecs/cs35l41-spi.c
-@@ -32,7 +32,6 @@ static int cs35l41_spi_probe(struct spi_device *spi)
- const struct regmap_config *regmap_config = &cs35l41_regmap_spi;
- struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(&spi->dev);
- struct cs35l41_private *cs35l41;
-- int ret;
-
- cs35l41 = devm_kzalloc(&spi->dev, sizeof(struct cs35l41_private), GFP_KERNEL);
- if (!cs35l41)
-@@ -43,11 +42,9 @@ static int cs35l41_spi_probe(struct spi_device *spi)
-
- spi_set_drvdata(spi, cs35l41);
- cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config);
-- if (IS_ERR(cs35l41->regmap)) {
-- ret = PTR_ERR(cs35l41->regmap);
-- dev_err(&spi->dev, "Failed to allocate register map: %d\n", ret);
-- return ret;
-- }
-+ if (IS_ERR(cs35l41->regmap))
-+ return dev_err_probe(cs35l41->dev, PTR_ERR(cs35l41->regmap),
-+ "Failed to allocate register map\n");
-
- cs35l41->dev = &spi->dev;
- cs35l41->irq = spi->irq;
-diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
-index 5456e6bfa242..7ddaa9bd8911 100644
---- a/sound/soc/codecs/cs35l41.c
-+++ b/sound/soc/codecs/cs35l41.c
-@@ -1190,16 +1190,14 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
-
- ret = devm_regulator_bulk_get(cs35l41->dev, CS35L41_NUM_SUPPLIES,
- cs35l41->supplies);
-- if (ret != 0) {
-- dev_err(cs35l41->dev, "Failed to request core supplies: %d\n", ret);
-- return ret;
-- }
-+ if (ret != 0)
-+ return dev_err_probe(cs35l41->dev, ret,
-+ "Failed to request core supplies\n");
-
- ret = regulator_bulk_enable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
-- if (ret != 0) {
-- dev_err(cs35l41->dev, "Failed to enable core supplies: %d\n", ret);
-- return ret;
-- }
-+ if (ret != 0)
-+ return dev_err_probe(cs35l41->dev, ret,
-+ "Failed to enable core supplies\n");
-
- /* returning NULL can be an option if in stereo mode */
- cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset",
-@@ -1211,8 +1209,8 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
- dev_info(cs35l41->dev,
- "Reset line busy, assuming shared reset\n");
- } else {
-- dev_err(cs35l41->dev,
-- "Failed to get reset GPIO: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret,
-+ "Failed to get reset GPIO\n");
- goto err;
- }
- }
-@@ -1228,8 +1226,8 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
- int_status, int_status & CS35L41_OTP_BOOT_DONE,
- 1000, 100000);
- if (ret) {
-- dev_err(cs35l41->dev,
-- "Failed waiting for OTP_BOOT_DONE: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret,
-+ "Failed waiting for OTP_BOOT_DONE\n");
- goto err;
- }
-
-@@ -1242,13 +1240,13 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
-
- ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
- if (ret < 0) {
-- dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Get Device ID failed\n");
- goto err;
- }
-
- ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
- if (ret < 0) {
-- dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Get Revision ID failed\n");
- goto err;
- }
-
-@@ -1273,7 +1271,7 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
-
- ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
- if (ret < 0) {
-- dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "OTP Unpack failed\n");
- goto err;
- }
-
-@@ -1293,13 +1291,13 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
- IRQF_ONESHOT | IRQF_SHARED | irq_pol,
- "cs35l41", cs35l41);
- if (ret != 0) {
-- dev_err(cs35l41->dev, "Failed to request IRQ: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Failed to request IRQ\n");
- goto err;
- }
-
- ret = cs35l41_set_pdata(cs35l41);
- if (ret < 0) {
-- dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Set pdata failed\n");
- goto err;
- }
-
-@@ -1322,7 +1320,7 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
- &soc_component_dev_cs35l41,
- cs35l41_dai, ARRAY_SIZE(cs35l41_dai));
- if (ret < 0) {
-- dev_err(cs35l41->dev, "Register codec failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Register codec failed\n");
- goto err_pm;
- }
-
---
-2.41.0
-From: Cristian Ciocaltea @ 2023-09-07 17:10 UTC (permalink / raw)
- To: James Schulman, David Rhodes, Richard Fitzgerald,
- Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
- Stefan Binding, Charles Keepax, Vitaly Rodionov
- Cc: alsa-devel, patches, linux-kernel, kernel
-
-Make use of the recently introduced EXPORT_GPL_DEV_PM_OPS() macro, to
-conditionally export the runtime/system PM functions.
-
-Replace the old SET_{RUNTIME,SYSTEM_SLEEP,NOIRQ_SYSTEM_SLEEP}_PM_OPS()
-helpers with their modern alternatives and get rid of the now
-unnecessary '__maybe_unused' annotations on all PM functions.
-
-Additionally, use the pm_ptr() macro to fix the following errors when
-building with CONFIG_PM disabled:
-
-ERROR: modpost: "cs35l41_pm_ops" [sound/soc/codecs/snd-soc-cs35l41-spi.ko] undefined!
-ERROR: modpost: "cs35l41_pm_ops" [sound/soc/codecs/snd-soc-cs35l41-i2c.ko] undefined!
-
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
-Acked-by: Charles Keepax <ckeepax@opensource.cirrus.com>
----
- sound/soc/codecs/cs35l41-i2c.c | 2 +-
- sound/soc/codecs/cs35l41-spi.c | 2 +-
- sound/soc/codecs/cs35l41.c | 21 ++++++++++-----------
- 3 files changed, 12 insertions(+), 13 deletions(-)
-
-diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
-index 9109203a7f25..96414ee35285 100644
---- a/sound/soc/codecs/cs35l41-i2c.c
-+++ b/sound/soc/codecs/cs35l41-i2c.c
-@@ -80,7 +80,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
- static struct i2c_driver cs35l41_i2c_driver = {
- .driver = {
- .name = "cs35l41",
-- .pm = &cs35l41_pm_ops,
-+ .pm = pm_ptr(&cs35l41_pm_ops),
- .of_match_table = of_match_ptr(cs35l41_of_match),
- .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
- },
-diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
-index 28e9c9473e60..a6db44520c06 100644
---- a/sound/soc/codecs/cs35l41-spi.c
-+++ b/sound/soc/codecs/cs35l41-spi.c
-@@ -80,7 +80,7 @@ MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match);
- static struct spi_driver cs35l41_spi_driver = {
- .driver = {
- .name = "cs35l41",
-- .pm = &cs35l41_pm_ops,
-+ .pm = pm_ptr(&cs35l41_pm_ops),
- .of_match_table = of_match_ptr(cs35l41_of_match),
- .acpi_match_table = ACPI_PTR(cs35l41_acpi_match),
- },
-diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
-index 7ddaa9bd8911..4bc64ba71cd6 100644
---- a/sound/soc/codecs/cs35l41.c
-+++ b/sound/soc/codecs/cs35l41.c
-@@ -1368,7 +1368,7 @@ void cs35l41_remove(struct cs35l41_private *cs35l41)
- }
- EXPORT_SYMBOL_GPL(cs35l41_remove);
-
--static int __maybe_unused cs35l41_runtime_suspend(struct device *dev)
-+static int cs35l41_runtime_suspend(struct device *dev)
- {
- struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
-
-@@ -1385,7 +1385,7 @@ static int __maybe_unused cs35l41_runtime_suspend(struct device *dev)
- return 0;
- }
-
--static int __maybe_unused cs35l41_runtime_resume(struct device *dev)
-+static int cs35l41_runtime_resume(struct device *dev)
- {
- struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
- int ret;
-@@ -1414,7 +1414,7 @@ static int __maybe_unused cs35l41_runtime_resume(struct device *dev)
- return 0;
- }
-
--static int __maybe_unused cs35l41_sys_suspend(struct device *dev)
-+static int cs35l41_sys_suspend(struct device *dev)
- {
- struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
-
-@@ -1424,7 +1424,7 @@ static int __maybe_unused cs35l41_sys_suspend(struct device *dev)
- return 0;
- }
-
--static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev)
-+static int cs35l41_sys_suspend_noirq(struct device *dev)
- {
- struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
-
-@@ -1434,7 +1434,7 @@ static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev)
- return 0;
- }
-
--static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev)
-+static int cs35l41_sys_resume_noirq(struct device *dev)
- {
- struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
-
-@@ -1444,7 +1444,7 @@ static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev)
- return 0;
- }
-
--static int __maybe_unused cs35l41_sys_resume(struct device *dev)
-+static int cs35l41_sys_resume(struct device *dev)
- {
- struct cs35l41_private *cs35l41 = dev_get_drvdata(dev);
-
-@@ -1454,13 +1454,12 @@ static int __maybe_unused cs35l41_sys_resume(struct device *dev)
- return 0;
- }
-
--const struct dev_pm_ops cs35l41_pm_ops = {
-- SET_RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
-+EXPORT_GPL_DEV_PM_OPS(cs35l41_pm_ops) = {
-+ RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
-
-- SET_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume)
-- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq)
-+ SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume)
-+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq)
- };
--EXPORT_SYMBOL_GPL(cs35l41_pm_ops);
-
- MODULE_DESCRIPTION("ASoC CS35L41 driver");
- MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
---
-2.41.0
-From: Cristian Ciocaltea @ 2023-09-07 17:10 UTC (permalink / raw)
- To: James Schulman, David Rhodes, Richard Fitzgerald,
- Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
- Stefan Binding, Charles Keepax, Vitaly Rodionov
- Cc: alsa-devel, patches, linux-kernel, kernel
-
-Replace the remaining dev_err() calls in probe() with dev_err_probe(),
-to improve consistency.
-
-Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
----
- sound/pci/hda/cs35l41_hda.c | 14 +++++++-------
- 1 file changed, 7 insertions(+), 7 deletions(-)
-
-diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
-index 565f7b897436..c74faa2ff46c 100644
---- a/sound/pci/hda/cs35l41_hda.c
-+++ b/sound/pci/hda/cs35l41_hda.c
-@@ -1550,27 +1550,27 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
- ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, int_status,
- int_status & CS35L41_OTP_BOOT_DONE, 1000, 100000);
- if (ret) {
-- dev_err(cs35l41->dev, "Failed waiting for OTP_BOOT_DONE: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Failed waiting for OTP_BOOT_DONE\n");
- goto err;
- }
-
- ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_sts);
- if (ret || (int_sts & CS35L41_OTP_BOOT_ERR)) {
-- dev_err(cs35l41->dev, "OTP Boot status %x error: %d\n",
-- int_sts & CS35L41_OTP_BOOT_ERR, ret);
-+ dev_err_probe(cs35l41->dev, ret, "OTP Boot status %x error\n",
-+ int_sts & CS35L41_OTP_BOOT_ERR);
- ret = -EIO;
- goto err;
- }
-
- ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
- if (ret) {
-- dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Get Device ID failed\n");
- goto err;
- }
-
- ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
- if (ret) {
-- dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Get Revision ID failed\n");
- goto err;
- }
-
-@@ -1593,7 +1593,7 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
-
- ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
- if (ret) {
-- dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "OTP Unpack failed\n");
- goto err;
- }
-
-@@ -1624,7 +1624,7 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
-
- ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops);
- if (ret) {
-- dev_err(cs35l41->dev, "Register component failed: %d\n", ret);
-+ dev_err_probe(cs35l41->dev, ret, "Register component failed\n");
- goto err_pm;
- }
-
---
-2.41.0
diff --git a/SOURCES/rog-ally-audio-fix.patch b/SOURCES/rog-ally-audio-fix.patch
new file mode 100644
index 0000000..9626c61
--- /dev/null
+++ b/SOURCES/rog-ally-audio-fix.patch
@@ -0,0 +1,64 @@
+diff --git a/sound/pci/hda/cd35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c
+index 6b704fd..d63617f 100644
+--- a/sound/pci/hda/cs35l41_hda_property.c
++++ b/sound/pci/hda/cs35l41_hda_property.c
+@@ -6,7 +6,9 @@
+ //
+ // Author: Stefan Binding <sbinding@opensource.cirrus.com>
+
++#include <linux/dmi.h>
+ #include <linux/gpio/consumer.h>
++#include <linux/kernel.h>
+ #include <linux/string.h>
+ #include "cs35l41_hda_property.h"
+
+@@ -117,6 +119,40 @@ static int asus_rog_2023_no_acpi(struct cs35l41_hda *cs35l41, struct device *phy
+ return 0;
+ }
+
++static int asus_rog_2023_ally_fix(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
++ const char *hid)
++{
++ const char *rog_ally_bios_ver = dmi_get_system_info(DMI_BIOS_VERSION);
++ const char *rog_ally_bios_num = rog_ally_bios_ver + 6; // Dropping the RC71L. part before the number
++ int rog_ally_bios_int;
++ kstrtoint(rog_ally_bios_num, 10, &rog_ally_bios_int);
++ if(rog_ally_bios_int >= 330){
++ printk(KERN_INFO "DSD properties exist in the %d BIOS. Not applying DSD override...\n", rog_ally_bios_int);
++ return -ENOENT; //Patch not applicable. Exiting...
++ }
++
++ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
++
++ dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id);
++
++ cs35l41->index = id == 0x40 ? 0 : 1;
++ cs35l41->channel_index = 0;
++ cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
++ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
++ hw_cfg->spk_pos = cs35l41->index;
++ hw_cfg->gpio1.func = CS35L41_NOT_USED;
++ hw_cfg->gpio1.valid = true;
++ hw_cfg->gpio2.func = CS35L41_INTERRUPT;
++ hw_cfg->gpio2.valid = true;
++ hw_cfg->bst_type = CS35L41_INT_BOOST;
++ hw_cfg->bst_ind = 1000; /* 1,000nH Inductance value */
++ hw_cfg->bst_ipk = 4500; /* 4,500mA peak current */
++ hw_cfg->bst_cap = 24; /* 24 microFarad cap value */
++ hw_cfg->valid = true;
++
++ return 0;
++}
++
+ struct cs35l41_prop_model {
+ const char *hid;
+ const char *ssid;
+@@ -134,7 +170,7 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
+ { "CSC3551", "10431483", asus_rog_2023_no_acpi }, // GU603V spi, rst=1, spkr=1
+ { "CSC3551", "10431493", asus_rog_2023_no_acpi }, // GV601V spi, rst=1
+ { "CSC3551", "10431573", asus_rog_2023_no_acpi }, // GZ301V spi, rst=0
+- { "CSC3551", "104317F3", asus_rog_2023_no_acpi }, // ROG ALLY i2c, rst=0
++ { "CSC3551", "104317F3", asus_rog_2023_ally_fix }, // ASUS ROG ALLY - i2c, rst=0
+ { "CSC3551", "10431B93", asus_rog_2023_no_acpi }, // G614J spi, rst=0
+ { "CSC3551", "10431C9F", asus_rog_2023_no_acpi }, // G614JI spi, rst=0
+ { "CSC3551", "10431CAF", asus_rog_2023_no_acpi }, // G634J spi, rst=1
diff --git a/SOURCES/rog-ally-bmc150.patch b/SOURCES/rog-ally-bmc150.patch
new file mode 100644
index 0000000..e83d6e3
--- /dev/null
+++ b/SOURCES/rog-ally-bmc150.patch
@@ -0,0 +1,2672 @@
+From 622ea77bfccd751247b1c08c3126d7ab716f0423 Mon Sep 17 00:00:00 2001
+From: Denis <benato.denis96@gmail.com>
+Date: Mon, 25 Sep 2023 03:38:49 +0200
+Subject: [PATCH] This commit adds support to the bmi323 device on top of the
+ pre-existing bmc150 kernel module.
+
+Some new devices for example the ROG Ally and the Air Plus identify this chip in the ACPI table as a bmc150 so previously the original module was loaded,
+but was erroring out as it cannot handle such device.
+
+The device I own does not allow me to use the interrupt part of the device as the interrupt pin is not connected (or not advertised to be connected) hence
+I avoided including on this commit anything related to IRQ.
+
+This driver has already been proved to work well enough to be used in the switch emulator "yuzu".
+
+While designing this module my main focus was not to alter the original driver and not to limit the original author in regard to future mofications,
+and I was mostly able to achive this, except:
+1) I added a new structure on top of the original one and added a field that is responsible for holding information
+on what type of chip the module is currently managing
+2) the previous point required the init function of the original driver to write that field in order to be sure no bmi323 code
+was executed when the old part of the module is managing the device
+3) as the original driver issued an i2c write on some register not really meant to be written in the bmi323 device I have made sure an i2c read to discover
+the bmi323 is performed prior to that code: such read SHOULD fail in the older bmc150 IC for two reasons:
+ - the i2c address is not reported in the memory map of the bmc150 in its datasheet
+ - the i2c read attempts to get 4 bytes out of a 8-bit device
+ - the fourth bit (the one that cannot be read from a bmc150 device) is initialized to 0 and bmi323 presence is signaled with a 1 in the LSB
+ that is the fourth coming out of the device in temporal order
+---
+ drivers/iio/accel/bmc150-accel-core.c | 2307 ++++++++++++++++++++++++-
+ drivers/iio/accel/bmc150-accel-i2c.c | 100 +-
+ drivers/iio/accel/bmc150-accel.h | 94 +-
+ 3 files changed, 2495 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
+index 110591804b4c..9a2c1732c9ef 100644
+--- a/drivers/iio/accel/bmc150-accel-core.c
++++ b/drivers/iio/accel/bmc150-accel-core.c
+@@ -130,6 +130,73 @@
+ #define BMC150_ACCEL_REG_FIFO_DATA 0x3F
+ #define BMC150_ACCEL_FIFO_LENGTH 32
+
++#define BMC150_BMI323_TEMPER_CENTER_VAL 23
++#define BMC150_BMI323_TEMPER_LSB_PER_KELVIN_VAL 512
++
++#define BMC150_BMI323_AUTO_SUSPEND_DELAY_MS 2000
++
++#define BMC150_BMI323_CHIP_ID_REG 0x00
++#define BMC150_BMI323_SOFT_RESET_REG 0x7E
++#define BMC150_BMI323_SOFT_RESET_VAL 0xDEAFU
++#define BMC150_BMI323_DATA_BASE_REG 0x03
++#define BMC150_BMI323_TEMPERATURE_DATA_REG 0x09
++#define BMC150_BMI323_FIFO_FILL_LEVEL_REG 0x15
++#define BMC150_BMI323_FIFO_DATA_REG 0x16
++#define BMC150_BMI323_ACC_CONF_REG 0x20
++#define BMC150_BMI323_GYR_CONF_REG 0x21
++#define BMC150_BMI323_FIFO_CONF_REG 0x36
++
++// these are bits [0:3] of ACC_CONF.acc_odr, sample rate in Hz for the accel part of the chip
++#define BMC150_BMI323_ACCEL_ODR_0_78123_VAL 0x0001
++#define BMC150_BMI323_ACCEL_ODR_1_5625_VAL 0x0002
++#define BMC150_BMI323_ACCEL_ODR_3_125_VAL 0x0003
++#define BMC150_BMI323_ACCEL_ODR_6_25_VAL 0x0004
++#define BMC150_BMI323_ACCEL_ODR_12_5_VAL 0x0005
++#define BMC150_BMI323_ACCEL_ODR_25_VAL 0x0006
++#define BMC150_BMI323_ACCEL_ODR_50_VAL 0x0007
++#define BMC150_BMI323_ACCEL_ODR_100_VAL 0x0008
++#define BMC150_BMI323_ACCEL_ODR_200_VAL 0x0009
++#define BMC150_BMI323_ACCEL_ODR_400_VAL 0x000A
++#define BMC150_BMI323_ACCEL_ODR_800_VAL 0x000B
++#define BMC150_BMI323_ACCEL_ODR_1600_VAL 0x000C
++#define BMC150_BMI323_ACCEL_ODR_3200_VAL 0x000D
++#define BMC150_BMI323_ACCEL_ODR_6400_VAL 0x000E
++
++#define BMC150_BMI323_ACCEL_BW_ODR_2_VAL 0x0000
++#define BMC150_BMI323_ACCEL_BW_ODR_4_VAL 0x0001
++
++// these are bits [4:6] of ACC_CONF.acc_range, full scale resolution
++#define BMC150_BMI323_ACCEL_RANGE_2_VAL 0x0000 // +/-2g, 16.38 LSB/mg
++#define BMC150_BMI323_ACCEL_RANGE_4_VAL 0x0001 // +/-4g, 8.19 LSB/mg
++#define BMC150_BMI323_ACCEL_RANGE_8_VAL 0x0002 // +/-8g, 4.10 LSB/mg
++#define BMC150_BMI323_ACCEL_RANGE_16_VAL 0x0003 // +/-4g, 2.05 LSB/mg
++
++// these are bits [0:3] of GYR_CONF.gyr_odr, sample rate in Hz for the gyro part of the chip
++#define BMC150_BMI323_GYRO_ODR_0_78123_VAL 0x0001
++#define BMC150_BMI323_GYRO_ODR_1_5625_VAL 0x0002
++#define BMC150_BMI323_GYRO_ODR_3_125_VAL 0x0003
++#define BMC150_BMI323_GYRO_ODR_6_25_VAL 0x0004
++#define BMC150_BMI323_GYRO_ODR_12_5_VAL 0x0005
++#define BMC150_BMI323_GYRO_ODR_25_VAL 0x0006
++#define BMC150_BMI323_GYRO_ODR_50_VAL 0x0007
++#define BMC150_BMI323_GYRO_ODR_100_VAL 0x0008
++#define BMC150_BMI323_GYRO_ODR_200_VAL 0x0009
++#define BMC150_BMI323_GYRO_ODR_400_VAL 0x000A
++#define BMC150_BMI323_GYRO_ODR_800_VAL 0x000B
++#define BMC150_BMI323_GYRO_ODR_1600_VAL 0x000C
++#define BMC150_BMI323_GYRO_ODR_3200_VAL 0x000D
++#define BMC150_BMI323_GYRO_ODR_6400_VAL 0x000E
++
++#define BMC150_BMI323_GYRO_BW_ODR_2_VAL 0x0000
++#define BMC150_BMI323_GYRO_BW_ODR_4_VAL 0x0001
++
++// these are bits [4:6] of GYR_CONF.gyr_range, full scale resolution
++#define BMC150_BMI323_GYRO_RANGE_125_VAL 0x0000 // +/-125°/s, 262.144 LSB/°/s
++#define BMC150_BMI323_GYRO_RANGE_250_VAL 0x0001 // +/-250°/s, 131.2 LSB/°/s
++#define BMC150_BMI323_GYRO_RANGE_500_VAL 0x0002 // +/-500°/s, 65.6 LSB/°/s
++#define BMC150_BMI323_GYRO_RANGE_1000_VAL 0x0003 // +/-1000°/s, 32.8 LSB/°/s
++#define BMC150_BMI323_GYRO_RANGE_2000_VAL 0x0004 // +/-2000°/s, 16.4 LSB/°/s
++
+ enum bmc150_accel_axis {
+ AXIS_X,
+ AXIS_Y,
+@@ -149,6 +216,654 @@ struct bmc150_scale_info {
+ u8 reg_range;
+ };
+
++/*
++ * This enum MUST not be altered as there are parts in the code that
++ * uses an int conversion to get the correct device register to read.
++ */
++enum bmi323_axis {
++ BMI323_ACCEL_AXIS_X = 0,
++ BMI323_ACCEL_AXIS_Y,
++ BMI323_ACCEL_AXIS_Z,
++ BMI323_GYRO_AXIS_X,
++ BMI323_GYRO_AXIS_Y,
++ BMI323_GYRO_AXIS_Z,
++ BMI323_TEMP,
++ BMI323_AXIS_MAX,
++};
++
++static const struct bmi323_scale_accel_info {
++ u8 hw_val;
++ int val;
++ int val2;
++ int ret_type;
++} bmi323_accel_scale_map[] = {
++ {
++ .hw_val = (u16)BMC150_BMI323_ACCEL_RANGE_2_VAL << (u16)4,
++ .val = 0,
++ .val2 = 598,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_ACCEL_RANGE_4_VAL << (u16)4,
++ .val = 0,
++ .val2 = 1196,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_ACCEL_RANGE_8_VAL << (u16)4,
++ .val = 0,
++ .val2 = 2392,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_ACCEL_RANGE_16_VAL << (u16)4,
++ .val = 0,
++ .val2 = 4785,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++};
++
++static const struct bmi323_scale_gyro_info {
++ u8 hw_val;
++ int val;
++ int val2;
++ int ret_type;
++} bmi323_gyro_scale_map[] = {
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_125_VAL << (u16)4,
++ .val = 0,
++ .val2 = 66545,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_125_VAL << (u16)4,
++ .val = 0,
++ .val2 = 66,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_250_VAL << (u16)4,
++ .val = 0,
++ .val2 = 133090,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_250_VAL << (u16)4,
++ .val = 0,
++ .val2 = 133,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_500_VAL << (u16)4,
++ .val = 0,
++ .val2 = 266181,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_500_VAL << (u16)4,
++ .val = 0,
++ .val2 = 266,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_1000_VAL << (u16)4,
++ .val = 0,
++ .val2 = 532362,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_1000_VAL << (u16)4,
++ .val = 0,
++ .val2 = 532,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_2000_VAL << (u16)4,
++ .val = 0,
++ .val2 = 1064724,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ // this shouldn't be necessary, but iio seems to have a wrong rounding of this value...
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_2000_VAL << (u16)4,
++ .val = 0,
++ .val2 = 1064,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++ {
++ .hw_val = (u16)BMC150_BMI323_GYRO_RANGE_2000_VAL << (u16)4,
++ .val = 0,
++ .val2 = 1065,
++ .ret_type = IIO_VAL_INT_PLUS_NANO,
++ },
++};
++
++/*
++ * this reflects the frequency map that is following.
++ * For each index i of that map index i*2 and i*2+1 of of this
++ * holds ODR/2 and ODR/4
++ */
++static const struct bmi323_3db_freq_cutoff_accel_info {
++ int val;
++ int val2;
++ int ret_type;
++} bmi323_accel_3db_freq_cutoff[] = {
++ {
++ .val = 0,
++ .val2 = 390615,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 0,
++ .val2 = 195308,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 0,
++ .val2 = 781300,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 0,
++ .val2 = 390650,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1,
++ .val2 = 562500,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 0,
++ .val2 = 78125,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 3,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1,
++ .val2 = 500000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 6,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 3,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 12,
++ .val2 = 500000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 6,
++ .val2 = 250000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 25,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 12,
++ .val2 = 500000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 50,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 25,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 100,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 50,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 200,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 100,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 400,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 200,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 800,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 400,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1600,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 800,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1600,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 800,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 3200,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1600,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++};
++
++static const struct bmi323_freq_accel_info {
++ u8 hw_val;
++ int val;
++ int val2;
++ s64 time_ns;
++} bmi323_accel_odr_map[] = {
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_0_78123_VAL,
++ .val = 0,
++ .val2 = 781230,
++ .time_ns = 1280032769,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_1_5625_VAL,
++ .val = 1,
++ .val2 = 562600,
++ .time_ns = 886522247,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_3_125_VAL,
++ .val = 3,
++ .val2 = 125000,
++ .time_ns = 320000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_6_25_VAL,
++ .val = 6,
++ .val2 = 250000,
++ .time_ns = 160000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_12_5_VAL,
++ .val = 12,
++ .val2 = 500000,
++ .time_ns = 80000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_25_VAL,
++ .val = 25,
++ .val2 = 0,
++ .time_ns = 40000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_50_VAL,
++ .val = 50,
++ .val2 = 0,
++ .time_ns = 20000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_100_VAL,
++ .val = 100,
++ .val2 = 0,
++ .time_ns = 10000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_200_VAL,
++ .val = 200,
++ .val2 = 0,
++ .time_ns = 5000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_400_VAL,
++ .val = 400,
++ .val2 = 0,
++ .time_ns = 2500000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_800_VAL,
++ .val = 800,
++ .val2 = 0,
++ .time_ns = 1250000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_1600_VAL,
++ .val = 1600,
++ .val2 = 0,
++ .time_ns = 625000,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_3200_VAL,
++ .val = 3200,
++ .val2 = 0,
++ .time_ns = 312500,
++ },
++ {
++ .hw_val = BMC150_BMI323_ACCEL_ODR_6400_VAL,
++ .val = 6400,
++ .val2 = 0,
++ .time_ns = 156250,
++ },
++};
++
++static const struct bmi323_freq_gyro_info {
++ u8 hw_val;
++ int val;
++ int val2;
++ s64 time_ns;
++} bmi323_gyro_odr_map[] = {
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_0_78123_VAL,
++ .val = 0,
++ .val2 = 781230,
++ .time_ns = 1280032769,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_1_5625_VAL,
++ .val = 1,
++ .val2 = 562600,
++ .time_ns = 886522247,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_3_125_VAL,
++ .val = 3,
++ .val2 = 125000,
++ .time_ns = 320000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_6_25_VAL,
++ .val = 6,
++ .val2 = 250000,
++ .time_ns = 160000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_12_5_VAL,
++ .val = 12,
++ .val2 = 500000,
++ .time_ns = 80000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_25_VAL,
++ .val = 25,
++ .val2 = 0,
++ .time_ns = 40000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_50_VAL,
++ .val = 50,
++ .val2 = 0,
++ .time_ns = 20000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_100_VAL,
++ .val = 100,
++ .val2 = 0,
++ .time_ns = 10000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_200_VAL,
++ .val = 200,
++ .val2 = 0,
++ .time_ns = 5000000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_400_VAL,
++ .val = 400,
++ .val2 = 0,
++ .time_ns = 2500000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_800_VAL,
++ .val = 800,
++ .val2 = 0,
++ .time_ns = 1250000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_1600_VAL,
++ .val = 1600,
++ .val2 = 0,
++ .time_ns = 625000,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_3200_VAL,
++ .val = 3200,
++ .val2 = 0,
++ .time_ns = 312500,
++ },
++ {
++ .hw_val = BMC150_BMI323_GYRO_ODR_6400_VAL,
++ .val = 6400,
++ .val2 = 0,
++ .time_ns = 156250,
++ },
++};
++
++static const struct bmi323_3db_freq_cutoff_gyro_info {
++ int val;
++ int val2;
++ int ret_type;
++} bmi323_gyro_3db_freq_cutoff[] = {
++ {
++ .val = 0,
++ .val2 = 390615,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 0,
++ .val2 = 1953075, // TODO: check if this gets reported correctly...
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 0,
++ .val2 = 781300,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 0,
++ .val2 = 390650,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1,
++ .val2 = 562500,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 0,
++ .val2 = 78125,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 3,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1,
++ .val2 = 500000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 6,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 3,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 12,
++ .val2 = 500000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 6,
++ .val2 = 250000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 25,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 12,
++ .val2 = 500000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 50,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 25,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 100,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 50,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 200,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 100,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 400,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 200,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 800,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 400,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1600,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 800,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1600,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 800,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 3200,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++ {
++ .val = 1600,
++ .val2 = 000000,
++ .ret_type = IIO_VAL_INT_PLUS_MICRO,
++ },
++};
++
++static const int bmi323_accel_scales[] = {
++ 0, 598, 0, 1196, 0, 2392, 0, 4785,
++};
++
++static const int bmi323_gyro_scales[] = {
++ 0, 66545, 0, 133090, 0, 266181, 0, 532362, 0, 1064724,
++};
++
++static const int bmi323_sample_freqs[] = {
++ 0, 781230, 1, 562600, 3, 125000, 6, 250000, 12, 500000,
++ 25, 0, 50, 0, 100, 0, 200, 0, 400, 0,
++ 800, 0, 1600, 0, 3200, 0, 6400, 0,
++};
++
++static const struct {
++ int val;
++ int val2; // IIO_VAL_INT_PLUS_MICRO
++ u8 bw_bits;
++} bmi323_samp_freq_table[] = { { 15, 620000, 0x08 }, { 31, 260000, 0x09 },
++ { 62, 500000, 0x0A }, { 125, 0, 0x0B },
++ { 250, 0, 0x0C }, { 500, 0, 0x0D },
++ { 1000, 0, 0x0E }, { 2000, 0, 0x0F } };
++
+ struct bmc150_accel_chip_info {
+ const char *name;
+ u8 chip_id;
+@@ -1113,6 +1828,52 @@ static const struct iio_event_spec bmc150_accel_event = {
+ .num_event_specs = 1 \
+ }
+
++#define BMI323_ACCEL_CHANNEL(_axis, bits) \
++ { \
++ .type = IIO_ACCEL, .modified = 1, .channel2 = IIO_MOD_##_axis, \
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
++ .info_mask_shared_by_type = \
++ BIT(IIO_CHAN_INFO_SCALE) | \
++ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
++ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
++ .info_mask_shared_by_type_available = \
++ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
++ BIT(IIO_CHAN_INFO_SCALE), \
++ .scan_index = BMI323_ACCEL_AXIS_##_axis, \
++ .scan_type = { \
++ .sign = 's', \
++ .realbits = (bits), \
++ .storagebits = 16, \
++ .shift = 16 - (bits), \
++ .endianness = IIO_LE, \
++ }, \
++ }
++
++#define BMI323_GYRO_CHANNEL(_axis, bits) \
++ { \
++ .type = IIO_ANGL_VEL, .modified = 1, \
++ .channel2 = IIO_MOD_##_axis, \
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
++ .info_mask_shared_by_type = \
++ BIT(IIO_CHAN_INFO_SCALE) | \
++ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
++ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
++ .info_mask_shared_by_type_available = \
++ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
++ BIT(IIO_CHAN_INFO_SCALE), \
++ .scan_index = BMI323_GYRO_AXIS_##_axis, \
++ .scan_type = { \
++ .sign = 's', \
++ .realbits = (bits), \
++ .storagebits = 16, \
++ .shift = 16 - (bits), \
++ .endianness = IIO_LE, \
++ }, \
++ /*.ext_info = bmi323_accel_ext_info,*/ \
++ /*.event_spec = &bmi323_accel_event,*/ \
++ /*.num_event_specs = 1*/ \
++ }
++
+ #define BMC150_ACCEL_CHANNELS(bits) { \
+ { \
+ .type = IIO_TEMP, \
+@@ -1595,7 +2356,7 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret, i;
+ unsigned int val;
+-
++
+ /*
+ * Reset chip to get it in a known good state. A delay of 1.8ms after
+ * reset is required according to the data sheets of supported chips.
+@@ -1677,6 +2438,11 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
+ data = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
++ /*
++ * Setting the dev_type here is necessary to avoid having it left uninitialized
++ * and therefore potentially executing bmi323 functions for the original bmc150 model.
++ */
++ data->dev_type = BMC150;
+ data->regmap = regmap;
+ data->type = type;
+
+@@ -1826,12 +2592,1407 @@ void bmc150_accel_core_remove(struct device *dev)
+ }
+ EXPORT_SYMBOL_NS_GPL(bmc150_accel_core_remove, IIO_BMC150);
+
+-#ifdef CONFIG_PM_SLEEP
+-static int bmc150_accel_suspend(struct device *dev)
++struct device *bmi323_get_managed_device(struct bmi323_private_data *bmi323)
++{
++ if (bmi323->i2c_client != NULL)
++ return &bmi323->i2c_client->dev;
++
++ return &bmi323->spi_client->dev;
++}
++
++static int bmi323_set_power_state(struct bmi323_private_data *bmi323, bool on)
++{
++#ifdef CONFIG_PM
++ struct device *dev = bmi323_get_managed_device(bmi323);
++ int ret;
++
++ if (on)
++ ret = pm_runtime_get_sync(dev);
++ else {
++ pm_runtime_mark_last_busy(dev);
++ ret = pm_runtime_put_autosuspend(dev);
++ }
++
++ if (ret < 0) {
++ dev_err(dev, "bmi323_set_power_state failed with %d\n", on);
++
++ if (on)
++ pm_runtime_put_noidle(dev);
++
++ return ret;
++ }
++#endif
++
++ return 0;
++}
++
++int bmi323_write_u16(struct bmi323_private_data *bmi323, u8 in_reg,
++ u16 in_value)
++{
++ s32 ret;
++
++ if (bmi323->i2c_client != NULL) {
++ ret = i2c_smbus_write_i2c_block_data(bmi323->i2c_client, in_reg,
++ sizeof(in_value),
++ (u8 *)(&in_value));
++ if (ret != 0) {
++ return -2;
++ }
++
++ return 0;
++ } else if (bmi323->spi_client != NULL) {
++ /*
++ * To whoever may need this: implementing this should be straightforward:
++ * it's specular to the i2c part.
++ */
++
++ return -EINVAL; // TODO: change with 0 once implemented
++ }
++
++ return -EINVAL;
++}
++EXPORT_SYMBOL_NS_GPL(bmi323_write_u16, IIO_BMC150);
++
++int bmi323_read_u16(struct bmi323_private_data *bmi323, u8 in_reg,
++ u16 *out_value)
++{
++ s32 ret;
++ u8 read_bytes[4];
++
++ if (bmi323->i2c_client != NULL) {
++ ret = i2c_smbus_read_i2c_block_data(bmi323->i2c_client, in_reg,
++ sizeof(read_bytes),
++ &read_bytes[0]);
++ if (ret != 4) {
++ return ret;
++ }
++
++ // DUMMY = read_bytes[0]
++ // DUMMY = read_bytes[1]
++ // LSB = read_bytes[2]
++ // MSB = read_bytes[3]
++ u8 *o = (u8 *)out_value;
++ o[0] = read_bytes[2];
++ o[1] = read_bytes[3];
++
++ return 0;
++ } else if (bmi323->spi_client != NULL) {
++ printk(KERN_CRIT
++ "bmi323: SPI interface is not yet implemented.\n");
++
++ /*
++ * To whoever may need this: implementing this should be straightforward:
++ * it's specular to the i2c part except that the dummy data is just 1 byte.
++ */
++
++ return -EINVAL; // TODO: change with 0 once implemented
++ }
++
++ return -EINVAL;
++}
++EXPORT_SYMBOL_NS_GPL(bmi323_read_u16, IIO_BMC150);
++
++int bmi323_chip_check(struct bmi323_private_data *bmi323)
++{
++ u16 chip_id;
++ int ret;
++
++ ret = bmi323_read_u16(bmi323, BMC150_BMI323_CHIP_ID_REG, &chip_id);
++ if (ret != 0) {
++ return ret;
++ }
++
++ if (((chip_id)&0x00FF) != cpu_to_le16((u16)0x0043U)) {
++ dev_err(bmi323->dev,
++ "bmi323_chip_check failed with: %d; chip_id = 0x%04x",
++ ret, chip_id);
++
++ return -EINVAL;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_NS_GPL(bmi323_chip_check, IIO_BMC150);
++
++static int bmi323_buffer_preenable(struct iio_dev *indio_dev)
++{
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++
++ const int ret = bmi323_set_power_state(&data->bmi323, true);
++
++ if (ret == 0) {
++ mutex_lock(&data->bmi323.mutex);
++ data->bmi323.fifo_frame_time_diff_ns =
++ (data->bmi323.acc_odr_time_ns >=
++ data->bmi323.gyr_odr_time_ns) ?
++ data->bmi323.acc_odr_time_ns :
++ data->bmi323.gyr_odr_time_ns;
++ mutex_unlock(&data->bmi323.mutex);
++ }
++
++ return ret;
++}
++
++static int bmi323_buffer_postenable(struct iio_dev *indio_dev)
++{
++ //struct bmc150_accel_data *data = iio_priv(indio_dev);
++
++ /*
++ * This code is a placeholder until I can get a way to test it
++ */
++
++ return 0;
++}
++
++static int bmi323_buffer_predisable(struct iio_dev *indio_dev)
++{
++ //struct bmc150_accel_data *data = iio_priv(indio_dev);
++
++ /*
++ * This code is a placeholder until I can get a way to test it
++ */
++
++ return 0;
++}
++
++static int bmi323_buffer_postdisable(struct iio_dev *indio_dev)
++{
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++
++ return bmi323_set_power_state(&data->bmi323, true);
++}
++
++static const struct iio_buffer_setup_ops bmi323_buffer_ops = {
++ .preenable = bmi323_buffer_preenable,
++ .postenable = bmi323_buffer_postenable,
++ .predisable = bmi323_buffer_predisable,
++ .postdisable = bmi323_buffer_postdisable,
++};
++
++int bmi323_chip_rst(struct bmi323_private_data *bmi323)
++{
++ u16 sensor_status = 0x0000, device_status = 0x0000;
++ int ret;
++
++ ret = bmi323_write_u16(bmi323, BMC150_BMI323_SOFT_RESET_REG,
++ cpu_to_le16((u16)BMC150_BMI323_SOFT_RESET_VAL));
++ if (ret != 0) {
++ dev_err(bmi323->dev,
++ "bmi323: error while issuing the soft-reset command: %d",
++ ret);
++ return ret;
++ }
++
++ /* wait the specified amount of time... I agree with the bmc150 module: better safe than sorry. */
++ msleep(5);
++
++ // if the device is connected over SPI a dummy read is to be performed once after each reset
++ if (bmi323->spi_client != NULL) {
++ dev_info(bmi323->dev,
++ "issuing the dummy read to switch mode to SPI");
++
++ // do not even check the result of that... it's just a dummy read
++ bmi323_chip_check(bmi323);
++ }
++
++ ret = bmi323_chip_check(bmi323);
++ if (ret != 0) {
++ return ret;
++ }
++
++ /* now check the correct initialization status as per datasheet */
++ ret = bmi323_read_u16(bmi323, 0x01, &device_status);
++ if (ret != 0) {
++ return -EINVAL;
++ }
++
++ if ((device_status & cpu_to_le16((u16)0x00FFU)) !=
++ cpu_to_le16((u16)0x0000U)) {
++ dev_err(bmi323->dev,
++ "bmi323: device_status incorrect: %d; device_status = 0x%04x",
++ ret, device_status);
++
++ /* from the datasheet: power error */
++ return -EINVAL;
++ }
++
++ /* from the datasheet: power ok */
++ ret = bmi323_read_u16(bmi323, 0x02, &sensor_status);
++ if (ret != 0) {
++ return -EINVAL;
++ }
++
++ if ((sensor_status & cpu_to_le16((u16)0x00FFU)) !=
++ cpu_to_le16((u16)0x0001U)) {
++ dev_err(bmi323->dev,
++ "bmi323: sensor_status incorrect: %d; sensor_status = 0x%04x",
++ ret, sensor_status);
++
++ /* from the datasheet: initialization error */
++ return -EINVAL;
++ }
++
++ /* from the datasheet: initialization ok */
++ return 0;
++}
++EXPORT_SYMBOL_NS_GPL(bmi323_chip_rst, IIO_BMC150);
++
++static const struct iio_chan_spec bmi323_channels[] = {
++ BMI323_ACCEL_CHANNEL(X, 16),
++ BMI323_ACCEL_CHANNEL(Y, 16),
++ BMI323_ACCEL_CHANNEL(Z, 16),
++ BMI323_GYRO_CHANNEL(X, 16),
++ BMI323_GYRO_CHANNEL(Y, 16),
++ BMI323_GYRO_CHANNEL(Z, 16),
++ {
++ .type = IIO_TEMP,
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
++ BIT(IIO_CHAN_INFO_SCALE) |
++ BIT(IIO_CHAN_INFO_OFFSET),
++ .scan_index = BMI323_TEMP,
++ },
++ IIO_CHAN_SOFT_TIMESTAMP(BMI323_AXIS_MAX),
++};
++
++static int bmi323_read_raw(struct iio_dev *indio_dev,
++ struct iio_chan_spec const *chan, int *val,
++ int *val2, long mask)
++{
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++ int ret = -EINVAL, was_sleep_modified = -1;
++ u16 raw_read = 0x8000;
++
++ mutex_lock(&data->bmi323.mutex);
++
++ if ((data->bmi323.flags & BMI323_FLAGS_RESET_FAILED) != 0x00U) {
++ dev_err(data->bmi323.dev,
++ "bmi323 error: device has not being woken up correctly.");
++ mutex_unlock(&data->bmi323.mutex);
++ return -EBUSY;
++ }
++
++ switch (mask) {
++ case IIO_CHAN_INFO_RAW: {
++ switch (chan->type) {
++ case IIO_TEMP:
++ if (iio_buffer_enabled(indio_dev)) {
++ ret = -EBUSY;
++ goto bmi323_read_raw_error;
++ }
++
++ was_sleep_modified =
++ bmi323_set_power_state(&data->bmi323, true);
++ if (was_sleep_modified != 0) {
++ ret = was_sleep_modified;
++ goto bmi323_read_raw_error_power;
++ }
++
++ ret = iio_device_claim_direct_mode(indio_dev);
++ if (ret != 0) {
++ printk(KERN_CRIT
++ "bmc150 bmi323_read_raw IIO_TEMP iio_device_claim_direct_mode returned %d\n",
++ ret);
++ goto bmi323_read_raw_error;
++ }
++
++ ret = bmi323_read_u16(
++ &data->bmi323,
++ BMC150_BMI323_TEMPERATURE_DATA_REG, &raw_read);
++ iio_device_release_direct_mode(indio_dev);
++ if (ret != 0) {
++ printk(KERN_CRIT
++ "bmc150 bmi323_read_raw IIO_TEMP bmi323_read_u16 returned %d\n",
++ ret);
++ goto bmi323_read_raw_error;
++ }
++
++ *val = sign_extend32(le16_to_cpu(raw_read), 15);
++ bmi323_set_power_state(&data->bmi323, false);
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_INT;
++
++ case IIO_ACCEL:
++ if (iio_buffer_enabled(indio_dev)) {
++ ret = -EBUSY;
++ goto bmi323_read_raw_error;
++ }
++
++ was_sleep_modified =
++ bmi323_set_power_state(&data->bmi323, true);
++ if (was_sleep_modified != 0) {
++ ret = was_sleep_modified;
++ goto bmi323_read_raw_error_power;
++ }
++
++ ret = iio_device_claim_direct_mode(indio_dev);
++ if (ret != 0) {
++ printk(KERN_CRIT
++ "bmc150 bmi323_read_raw IIO_ACCEL iio_device_claim_direct_mode returned %d\n",
++ ret);
++ goto bmi323_read_raw_error;
++ }
++
++ ret = bmi323_read_u16(&data->bmi323,
++ BMC150_BMI323_DATA_BASE_REG +
++ (u8)(chan->scan_index),
++ &raw_read);
++ iio_device_release_direct_mode(indio_dev);
++ if (ret != 0) {
++ printk(KERN_CRIT
++ "bmc150 bmi323_read_raw IIO_ACCEL bmi323_read_u16 returned %d\n",
++ ret);
++ goto bmi323_read_raw_error;
++ }
++ *val = sign_extend32(le16_to_cpu(raw_read), 15);
++ bmi323_set_power_state(&data->bmi323, false);
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_INT;
++
++ case IIO_ANGL_VEL:
++ if (iio_buffer_enabled(indio_dev)) {
++ ret = -EBUSY;
++ goto bmi323_read_raw_error;
++ }
++
++ was_sleep_modified =
++ bmi323_set_power_state(&data->bmi323, true);
++ if (was_sleep_modified != 0) {
++ ret = was_sleep_modified;
++ goto bmi323_read_raw_error_power;
++ }
++
++ ret = iio_device_claim_direct_mode(indio_dev);
++ if (ret != 0) {
++ printk(KERN_CRIT
++ "bmc150 bmi323_read_raw IIO_ANGL_VEL iio_device_claim_direct_mode returned %d\n",
++ ret);
++ goto bmi323_read_raw_error;
++ }
++
++ ret = bmi323_read_u16(&data->bmi323,
++ BMC150_BMI323_DATA_BASE_REG +
++ (u8)(chan->scan_index),
++ &raw_read);
++ iio_device_release_direct_mode(indio_dev);
++ if (ret != 0) {
++ printk(KERN_CRIT
++ "bmc150 bmi323_read_raw IIO_ANGL_VEL bmi323_read_u16 returned %d\n",
++ ret);
++ goto bmi323_read_raw_error;
++ }
++
++ *val = sign_extend32(le16_to_cpu(raw_read), 15);
++ bmi323_set_power_state(&data->bmi323, false);
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_INT;
++
++ default:
++ goto bmi323_read_raw_error;
++ }
++ }
++ case IIO_CHAN_INFO_OFFSET: {
++ switch (chan->type) {
++ case IIO_TEMP:
++ *val = BMC150_BMI323_TEMPER_CENTER_VAL;
++ *val2 = 0;
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_INT;
++
++ default:
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ }
++ case IIO_CHAN_INFO_SCALE:
++ switch (chan->type) {
++ case IIO_TEMP: {
++ *val = 0;
++ *val2 = BMC150_BMI323_TEMPER_LSB_PER_KELVIN_VAL;
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_FRACTIONAL;
++ }
++ case IIO_ACCEL: {
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323.acc_conf_reg_value;
++ for (int s = 0; s < ARRAY_SIZE(bmi323_accel_scale_map);
++ ++s) {
++ if (((le_raw_read[0]) & ((u16)0b01110000U)) ==
++ (bmi323_accel_scale_map[s].hw_val)) {
++ *val = bmi323_accel_scale_map[s].val;
++ *val2 = bmi323_accel_scale_map[s].val2;
++
++ mutex_unlock(&data->bmi323.mutex);
++ return bmi323_accel_scale_map[s]
++ .ret_type;
++ }
++ }
++
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ case IIO_ANGL_VEL: {
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323.gyr_conf_reg_value;
++ for (int s = 0; s < ARRAY_SIZE(bmi323_gyro_scale_map);
++ ++s) {
++ if (((le_raw_read[0]) & ((u16)0b01110000U)) ==
++ (bmi323_gyro_scale_map[s].hw_val)) {
++ *val = bmi323_gyro_scale_map[s].val;
++ *val2 = bmi323_gyro_scale_map[s].val2;
++
++ mutex_unlock(&data->bmi323.mutex);
++ return bmi323_gyro_scale_map[s].ret_type;
++ }
++ }
++
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ default:
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
++ switch (chan->type) {
++ case IIO_ACCEL: {
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323.acc_conf_reg_value;
++ for (int s = 0; s < ARRAY_SIZE(bmi323_accel_odr_map);
++ ++s) {
++ if (((le_raw_read[0]) & ((u16)0x0FU)) ==
++ (bmi323_accel_odr_map[s].hw_val)) {
++ /*
++ * from tha datasheed: -3dB cut-off frequency can be configured with the bit 7 of GYR_confm,
++ * also called acc_bw that can either be 0 or 1, where 1 means odr/4 and 0 means odr/2
++ */
++ int freq_adj_idx =
++ (((le_raw_read[0]) &
++ ((u8)0x80U)) == (u8)0x00U) ?
++ (s * 2) + 0 :
++ (s * 2) + 1;
++ *val = bmi323_accel_3db_freq_cutoff
++ [freq_adj_idx]
++ .val;
++ *val2 = bmi323_accel_3db_freq_cutoff
++ [freq_adj_idx]
++ .val2;
++
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_INT_PLUS_MICRO;
++ }
++ }
++
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ case IIO_ANGL_VEL: {
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323.gyr_conf_reg_value;
++ for (int s = 0; s < ARRAY_SIZE(bmi323_gyro_odr_map);
++ ++s) {
++ if (((le_raw_read[0]) & ((u16)0x0FU)) ==
++ (bmi323_gyro_odr_map[s].hw_val)) {
++ /*
++ * from tha datasheed: -3dB cut-off frequency can be configured with the bit 7 of GYR_confm,
++ * also called acc_bw that can either be 0 or 1, where 1 means odr/4 and 0 means odr/2
++ */
++ int freq_adj_idx =
++ (((le_raw_read[0]) &
++ ((u8)0x80U)) == (u8)0x0000U) ?
++ (s * 2) + 0 :
++ (s * 2) + 1;
++ *val = bmi323_gyro_3db_freq_cutoff
++ [freq_adj_idx]
++ .val;
++ *val2 = bmi323_gyro_3db_freq_cutoff
++ [freq_adj_idx]
++ .val2;
++
++ mutex_unlock(&data->bmi323.mutex);
++ return bmi323_gyro_3db_freq_cutoff
++ [freq_adj_idx]
++ .ret_type;
++ }
++ }
++
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ default: {
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ }
++ case IIO_CHAN_INFO_SAMP_FREQ:
++ switch (chan->type) {
++ case IIO_TEMP: {
++
++ // while in normal or power mode the temperature sensur has a 50Hz sampling frequency
++ *val = 50;
++ *val2 = 0;
++
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_INT_PLUS_MICRO;
++ }
++ case IIO_ACCEL: {
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323.acc_conf_reg_value;
++ for (int s = 0; s < ARRAY_SIZE(bmi323_accel_odr_map);
++ ++s) {
++ if (((le_raw_read[0]) & ((u16)0x0FU)) ==
++ (bmi323_accel_odr_map[s].hw_val)) {
++ *val = bmi323_accel_odr_map[s].val;
++ *val2 = bmi323_accel_odr_map[s].val2;
++
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_INT_PLUS_MICRO;
++ }
++ }
++
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ case IIO_ANGL_VEL: {
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323.gyr_conf_reg_value;
++ for (int s = 0; s < ARRAY_SIZE(bmi323_gyro_odr_map);
++ ++s) {
++ if (((le_raw_read[0]) & ((u16)0x0FU)) ==
++ (bmi323_gyro_odr_map[s].hw_val)) {
++ *val = bmi323_gyro_odr_map[s].val;
++ *val2 = bmi323_gyro_odr_map[s].val2;
++
++ mutex_unlock(&data->bmi323.mutex);
++ return IIO_VAL_INT_PLUS_MICRO;
++ }
++ }
++
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ default:
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++ default:
++ ret = -EINVAL;
++ goto bmi323_read_raw_error;
++ }
++
++bmi323_read_raw_error:
++ if (was_sleep_modified == 0) {
++ bmi323_set_power_state(&data->bmi323, false);
++ }
++
++bmi323_read_raw_error_power:
++ mutex_unlock(&data->bmi323.mutex);
++ return ret;
++}
++
++static int bmi323_write_raw(struct iio_dev *indio_dev,
++ struct iio_chan_spec const *chan, int val, int val2,
++ long mask)
++{
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++ int ret = -EINVAL, was_sleep_modified = -1;
++
++ mutex_lock(&data->bmi323.mutex);
++
++ if ((data->bmi323.flags & BMI323_FLAGS_RESET_FAILED) != 0x00U) {
++ dev_err(data->bmi323.dev,
++ "bmi323 error: device has not being woken up correctly.");
++ mutex_unlock(&data->bmi323.mutex);
++ return -EBUSY;
++ }
++
++ switch (mask) {
++ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
++ switch (chan->type) {
++ default: {
++ ret = -EINVAL;
++ goto bmi323_write_raw_error;
++ }
++ }
++ case IIO_CHAN_INFO_SAMP_FREQ:
++ switch (chan->type) {
++ case IIO_ACCEL:
++ if (iio_buffer_enabled(indio_dev)) {
++ ret = -EBUSY;
++ goto bmi323_write_raw_error;
++ }
++
++ for (int s = 0; s < ARRAY_SIZE(bmi323_accel_odr_map);
++ ++s) {
++ if ((bmi323_accel_odr_map[s].val == val) &&
++ (bmi323_accel_odr_map[s].val2 == val2)) {
++ const u16 conf_backup =
++ data->bmi323.acc_conf_reg_value;
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323
++ .acc_conf_reg_value;
++ le_raw_read[0] &= (u8)0b11110000U;
++ le_raw_read[0] |=
++ ((u8)bmi323_gyro_odr_map[s]
++ .hw_val);
++
++ was_sleep_modified =
++ bmi323_set_power_state(
++ &data->bmi323, true);
++ if (was_sleep_modified != 0) {
++ ret = was_sleep_modified;
++ data->bmi323.acc_conf_reg_value =
++ conf_backup;
++ goto bmi323_write_raw_error_power;
++ }
++
++ ret = bmi323_write_u16(
++ &data->bmi323,
++ BMC150_BMI323_ACC_CONF_REG,
++ data->bmi323.acc_conf_reg_value);
++ if (ret != 0) {
++ data->bmi323.acc_conf_reg_value =
++ conf_backup;
++ goto bmi323_write_raw_error;
++ }
++
++ data->bmi323.acc_odr_time_ns =
++ bmi323_accel_odr_map[s].time_ns;
++ bmi323_set_power_state(&data->bmi323,
++ false);
++ mutex_unlock(&data->bmi323.mutex);
++ return 0;
++ }
++ }
++
++ ret = -EINVAL;
++ goto bmi323_write_raw_error;
++ case IIO_ANGL_VEL:
++ if (iio_buffer_enabled(indio_dev)) {
++ ret = -EBUSY;
++ goto bmi323_write_raw_error;
++ }
++
++ for (int s = 0; s < ARRAY_SIZE(bmi323_gyro_odr_map);
++ ++s) {
++ if ((bmi323_gyro_odr_map[s].val == val) &&
++ (bmi323_gyro_odr_map[s].val2 == val2)) {
++ const u16 conf_backup =
++ data->bmi323.gyr_conf_reg_value;
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323
++ .gyr_conf_reg_value;
++ le_raw_read[0] &= (u8)0b11110000U;
++ le_raw_read[0] |=
++ ((u8)bmi323_gyro_odr_map[s]
++ .hw_val);
++
++ was_sleep_modified =
++ bmi323_set_power_state(
++ &data->bmi323, true);
++ if (was_sleep_modified != 0) {
++ ret = was_sleep_modified;
++ data->bmi323.gyr_conf_reg_value =
++ conf_backup;
++ goto bmi323_write_raw_error_power;
++ }
++
++ ret = bmi323_write_u16(
++ &data->bmi323,
++ BMC150_BMI323_GYR_CONF_REG,
++ data->bmi323.gyr_conf_reg_value);
++ if (ret != 0) {
++ data->bmi323.gyr_conf_reg_value =
++ conf_backup;
++ goto bmi323_write_raw_error;
++ }
++
++ data->bmi323.gyr_odr_time_ns =
++ bmi323_gyro_odr_map[s].time_ns;
++ bmi323_set_power_state(&data->bmi323,
++ false);
++ mutex_unlock(&data->bmi323.mutex);
++ return 0;
++ }
++ }
++
++ ret = -EINVAL;
++ goto bmi323_write_raw_error;
++
++ /* Termometer also ends up here: its sampling frequency depends on the chip configuration and cannot be changed */
++ default:
++ ret = -EINVAL;
++ goto bmi323_write_raw_error;
++ }
++
++ break;
++ case IIO_CHAN_INFO_SCALE:
++ switch (chan->type) {
++ case IIO_ACCEL:
++ if (iio_buffer_enabled(indio_dev)) {
++ ret = -EBUSY;
++ goto bmi323_write_raw_error;
++ }
++
++ for (int s = 0; s < ARRAY_SIZE(bmi323_accel_scale_map);
++ ++s) {
++ if ((bmi323_accel_scale_map[s].val == val) &&
++ (bmi323_accel_scale_map[s].val2 == val2)) {
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323
++ .acc_conf_reg_value;
++ le_raw_read[0] &= (u8)0b10001111U;
++ le_raw_read[0] |=
++ ((u8)bmi323_accel_scale_map[s]
++ .hw_val);
++
++ was_sleep_modified =
++ bmi323_set_power_state(
++ &data->bmi323, true);
++ if (was_sleep_modified != 0) {
++ ret = was_sleep_modified;
++ goto bmi323_write_raw_error_power;
++ }
++
++ ret = bmi323_write_u16(
++ &data->bmi323,
++ BMC150_BMI323_ACC_CONF_REG,
++ data->bmi323.acc_conf_reg_value);
++ if (ret != 0) {
++ goto bmi323_write_raw_error;
++ }
++
++ bmi323_set_power_state(&data->bmi323,
++ false);
++ mutex_unlock(&data->bmi323.mutex);
++ return 0;
++ }
++ }
++
++ dev_warn(
++ data->bmi323.dev,
++ "bmi323 error: accel scale val=%d,val2=%d unavailable: ignoring.",
++ val, val2);
++
++ ret = -EINVAL;
++ goto bmi323_write_raw_error;
++ case IIO_ANGL_VEL:
++ if (iio_buffer_enabled(indio_dev)) {
++ ret = -EBUSY;
++ goto bmi323_write_raw_error;
++ }
++
++ for (int s = 0; s < ARRAY_SIZE(bmi323_gyro_scale_map);
++ ++s) {
++ if ((bmi323_gyro_scale_map[s].val == val) &&
++ (bmi323_gyro_scale_map[s].val2 == val2)) {
++ u8 *le_raw_read =
++ (u8 *)&data->bmi323
++ .gyr_conf_reg_value;
++ le_raw_read[0] &= (u8)0b10001111U;
++ le_raw_read[0] |=
++ ((u8)bmi323_gyro_scale_map[s]
++ .hw_val);
++
++ was_sleep_modified =
++ bmi323_set_power_state(
++ &data->bmi323, true);
++ if (was_sleep_modified != 0) {
++ ret = was_sleep_modified;
++ goto bmi323_write_raw_error_power;
++ }
++
++ ret = bmi323_write_u16(
++ &data->bmi323,
++ BMC150_BMI323_GYR_CONF_REG,
++ data->bmi323.acc_conf_reg_value);
++ if (ret != 0) {
++ goto bmi323_write_raw_error;
++ }
++
++ bmi323_set_power_state(&data->bmi323,
++ false);
++ mutex_unlock(&data->bmi323.mutex);
++ return 0;
++ }
++ }
++
++ dev_warn(
++ data->bmi323.dev,
++ "bmi323 error: gyro scale val=%d,val2=%d unavailable: ignoring.",
++ val, val2);
++
++ ret = -EINVAL;
++ goto bmi323_write_raw_error;
++
++ default:
++ ret = -EINVAL;
++ goto bmi323_write_raw_error;
++ }
++
++ default:
++ ret = -EINVAL;
++ goto bmi323_write_raw_error;
++ }
++
++bmi323_write_raw_error:
++ if (was_sleep_modified == 0) {
++ bmi323_set_power_state(&data->bmi323, false);
++ }
++
++bmi323_write_raw_error_power:
++ mutex_unlock(&data->bmi323.mutex);
++ return ret;
++}
++
++static int bmi323_read_avail(struct iio_dev *indio_dev,
++ struct iio_chan_spec const *chan, const int **vals,
++ int *type, int *length, long mask)
++{
++ switch (mask) {
++ case IIO_CHAN_INFO_SCALE:
++ switch (chan->type) {
++ case IIO_ACCEL:
++ *type = IIO_VAL_INT_PLUS_MICRO;
++ *vals = bmi323_accel_scales;
++ *length = ARRAY_SIZE(bmi323_accel_scales);
++ return IIO_AVAIL_LIST;
++ case IIO_ANGL_VEL:
++ *type = IIO_VAL_INT_PLUS_NANO;
++ *vals = bmi323_gyro_scales;
++ *length = ARRAY_SIZE(bmi323_gyro_scales);
++ return IIO_AVAIL_LIST;
++ default:
++ return -EINVAL;
++ }
++ case IIO_CHAN_INFO_SAMP_FREQ:
++ *type = IIO_VAL_INT_PLUS_MICRO;
++ *vals = bmi323_sample_freqs;
++ *length = ARRAY_SIZE(bmi323_sample_freqs);
++ return IIO_AVAIL_LIST;
++ default:
++ return -EINVAL;
++ }
++}
++
++static const struct iio_info bmi323_accel_info = {
++ .read_raw = bmi323_read_raw,
++ .write_raw = bmi323_write_raw,
++ .read_avail = bmi323_read_avail,
++ //.hwfifo_flush_to_buffer = bmi323_fifo_flush,
++};
++
++static int bmi323_fifo_flush(struct iio_dev *indio_dev)
++{
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++ int ret;
++
++ ret = bmi323_write_u16(&data->bmi323, 0x37, cpu_to_le16(0x01));
++
++ return ret;
++}
++
++static const u16 stub_value = 0x8000;
++
++#define ADVANCE_AT_REQ_OR_AVAIL(req, avail, dst, dst_offset, src, src_offset) \
++ if (req) { \
++ if (gyr_avail) { \
++ memcpy((void *)(dst + dst_offset), \
++ (const void *)(src + src_offset), 2); \
++ src_offset += 2; \
++ } else { \
++ memcpy((void *)(dst + dst_offset), \
++ (const void *)((const u8 *)(&stub_value)), 2); \
++ } \
++ dst_offset += 2; \
++ } else { \
++ if (avail) { \
++ src_offset += 2; \
++ } \
++ }
++
++static irqreturn_t iio_bmi323_trigger_h(int irq, void *p)
++{
++ printk(KERN_WARNING "bmi323 executed iio_bmi323_trigger_h");
++
++ struct iio_poll_func *pf = p;
++ struct iio_dev *indio_dev = pf->indio_dev;
++ struct bmc150_accel_data *indio_data = iio_priv(indio_dev);
++
++ mutex_lock(&indio_data->bmi323.mutex);
++
++ const bool temp_avail = ((indio_data->bmi323.fifo_conf_reg_value &
++ (cpu_to_le16(0b0000100000000000))) != 0);
++ const bool gyr_avail = ((indio_data->bmi323.fifo_conf_reg_value &
++ (cpu_to_le16(0b0000010000000000))) != 0);
++ const bool acc_avail = ((indio_data->bmi323.fifo_conf_reg_value &
++ (cpu_to_le16(0b0000001000000000))) != 0);
++ const bool time_avail = ((indio_data->bmi323.fifo_conf_reg_value &
++ (cpu_to_le16(0b0000000100000000))) != 0);
++
++ /* Calculate the number of bytes for a frame */
++ const u16 frames_aggregate_size_in_words =
++ /* 2 * */ ((temp_avail ? 1 : 0) + (gyr_avail ? 3 : 0) +
++ (acc_avail ? 3 : 0) + (time_avail ? 1 : 0));
++
++ u16 available_words = 0;
++ const int available_words_read_res = bmi323_read_u16(
++ &indio_data->bmi323, BMC150_BMI323_FIFO_FILL_LEVEL_REG,
++ &available_words);
++ if (available_words_read_res != 0) {
++ goto bmi323_irq_done;
++ }
++
++ const u16 available_frame_aggregates = (le16_to_cpu(available_words)) /
++ (frames_aggregate_size_in_words);
++
++ const s64 current_timestamp_ns = iio_get_time_ns(indio_dev);
++ const s64 fifo_frame_time_ns =
++ indio_data->bmi323.fifo_frame_time_diff_ns;
++ const s64 first_sample_timestamp_ns =
++ current_timestamp_ns -
++ (fifo_frame_time_ns * (s64)(available_frame_aggregates));
++
++ /* This can hold one full block */
++ u8 temp_data[16];
++
++ /* This is fifo data as read from the sensor */
++ u8 fifo_data[32];
++
++ /*
++ | CHANNEL | scan_index
++ |============================
++ | | |
++ | ACCEL_X | 0 |
++ | ACCEL_Y | 1 |
++ | ACCEL_Y | 2 |
++ | GYRO_X | 3 |
++ | GYRO_Y | 4 |
++ | GYRO_Z | 5 |
++ | TEMP | 6 |
++ | TIMESTAMP | ? |
++ */
++ bool accel_x_requested = false;
++ bool accel_y_requested = false;
++ bool accel_z_requested = false;
++ bool gyro_x_requested = false;
++ bool gyro_y_requested = false;
++ bool gyro_z_requested = false;
++ bool temp_requested = false;
++
++ int j = 0;
++ for_each_set_bit(j, indio_dev->active_scan_mask,
++ indio_dev->masklength) {
++ switch (j) {
++ case 0:
++ accel_x_requested = true;
++ break;
++ case 1:
++ accel_y_requested = true;
++ break;
++ case 2:
++ accel_z_requested = true;
++ break;
++ case 3:
++ gyro_x_requested = true;
++ break;
++ case 4:
++ gyro_y_requested = true;
++ break;
++ case 5:
++ gyro_z_requested = true;
++ break;
++ case 6:
++ temp_requested = true;
++ break;
++ default:
++ break;
++ }
++ }
++
++ u16 current_fifo_buffer_offset_bytes = 0;
++ for (u16 f = 0; f < available_frame_aggregates; ++f) {
++ u16 current_sample_buffer_offset = 0;
++
++ /* Read data from the raw device */
++ if (indio_data->bmi323.i2c_client != NULL) {
++ const int bytes_to_read =
++ 2 + (2 * frames_aggregate_size_in_words);
++ int read_block_ret = i2c_smbus_read_i2c_block_data(
++ indio_data->bmi323.i2c_client,
++ BMC150_BMI323_FIFO_DATA_REG, bytes_to_read,
++ &fifo_data[0]);
++ if (read_block_ret < bytes_to_read) {
++ dev_warn(
++ &indio_data->bmi323.i2c_client->dev,
++ "bmi323: i2c_smbus_read_i2c_block_data wrong return: expected %d bytes, %d arrived. Doing what is possible with recovered data.\n",
++ bytes_to_read, read_block_ret);
++
++ /* at this point FIFO buffer must be flushed to avoid interpreting data incorrectly the next trigger */
++ const int flush_res =
++ bmi323_fifo_flush(indio_dev);
++ if (flush_res != 0) {
++ dev_err(&indio_data->bmi323.i2c_client
++ ->dev,
++ "bmi323: Could not flush FIFO (%d). Following buffer data might be corrupted.\n",
++ flush_res);
++ }
++
++ goto bmi323_irq_done;
++ }
++
++ /* Discard 2-bytes dummy data from I2C */
++ current_fifo_buffer_offset_bytes = 2;
++ } else if (indio_data->bmi323.spi_client != NULL) {
++ printk(KERN_CRIT
++ "bmi323: SPI interface is not yet implemented.\n");
++
++ /*
++ * To whoever may need this: implementing this should be straightforward:
++ * it's specular to the i2c part.
++ */
++
++ /* Discard 1-byte dummy data from SPI */
++ current_fifo_buffer_offset_bytes = 1;
++
++ goto bmi323_irq_done;
++ }
++
++ ADVANCE_AT_REQ_OR_AVAIL(accel_x_requested, acc_avail,
++ (u8 *)&temp_data[0],
++ current_sample_buffer_offset,
++ (u8 *)&fifo_data[0],
++ current_fifo_buffer_offset_bytes);
++ ADVANCE_AT_REQ_OR_AVAIL(accel_y_requested, acc_avail,
++ (u8 *)&temp_data[0],
++ current_sample_buffer_offset,
++ (u8 *)&fifo_data[0],
++ current_fifo_buffer_offset_bytes);
++ ADVANCE_AT_REQ_OR_AVAIL(accel_z_requested, acc_avail,
++ (u8 *)&temp_data[0],
++ current_sample_buffer_offset,
++ (u8 *)&fifo_data[0],
++ current_fifo_buffer_offset_bytes);
++ ADVANCE_AT_REQ_OR_AVAIL(gyro_x_requested, gyr_avail,
++ (u8 *)&temp_data[0],
++ current_sample_buffer_offset,
++ (u8 *)&fifo_data[0],
++ current_fifo_buffer_offset_bytes);
++ ADVANCE_AT_REQ_OR_AVAIL(gyro_y_requested, gyr_avail,
++ (u8 *)&temp_data[0],
++ current_sample_buffer_offset,
++ (u8 *)&fifo_data[0],
++ current_fifo_buffer_offset_bytes);
++ ADVANCE_AT_REQ_OR_AVAIL(gyro_z_requested, gyr_avail,
++ (u8 *)&temp_data[0],
++ current_sample_buffer_offset,
++ (u8 *)&fifo_data[0],
++ current_fifo_buffer_offset_bytes);
++ ADVANCE_AT_REQ_OR_AVAIL(temp_requested, temp_avail,
++ (u8 *)&temp_data[0],
++ current_sample_buffer_offset,
++ (u8 *)&fifo_data[0],
++ current_fifo_buffer_offset_bytes);
++
++#ifdef BMC150_BMI232_DEBUG_EN
++ /* The following is code only used for debugging */
++ u16 timestamp = 0;
++ if (time_avail) {
++ memcpy((u8 *)&timestamp,
++ (const u8
++ *)(&fifo_data
++ [current_fifo_buffer_offset_bytes]),
++ 2);
++ current_fifo_buffer_offset_bytes += 2;
++ }
++
++ u16 *debg = (u16 *)&temp_data[0];
++ if (!time_avail) {
++ printk(KERN_WARNING
++ "bmi323 pushing to buffer %d/%d -- accel: %d %d %d gyro: %d %d %d",
++ (int)(f + 1), (int)available_frame_aggregates,
++ (int)(*((s16 *)&debg[0])),
++ (int)(*((s16 *)&debg[1])),
++ (int)(*((s16 *)&debg[2])),
++ (int)(*((s16 *)&debg[3])),
++ (int)(*((s16 *)&debg[4])),
++ (int)(*((s16 *)&debg[5])));
++ } else {
++ printk(KERN_WARNING
++ "bmi323 pushing to buffer %d/%d -- time: %d accel: %d %d %d gyro: %d %d %d",
++ (int)(f + 1), (int)available_frame_aggregates,
++ (int)timestamp, (int)(*((s16 *)&debg[0])),
++ (int)(*((s16 *)&debg[1])),
++ (int)(*((s16 *)&debg[2])),
++ (int)(*((s16 *)&debg[3])),
++ (int)(*((s16 *)&debg[4])),
++ (int)(*((s16 *)&debg[5])));
++ }
++#endif
++
++ iio_push_to_buffers_with_timestamp(
++ indio_dev, &temp_data[0],
++ first_sample_timestamp_ns +
++ (fifo_frame_time_ns * (s64)j));
++ }
++
++bmi323_irq_done:
++ mutex_unlock(&indio_data->bmi323.mutex);
++
++ /*
++ * Tell the core we are done with this trigger and ready for the
++ * next one.
++ */
++ iio_trigger_notify_done(indio_dev->trig);
++
++ return IRQ_HANDLED;
++}
++
++int bmi323_set_trigger_state(struct iio_trigger *trig, bool state)
++{
++ return 0;
++}
++
++/*
++// The following is meant to be used in a IRQ-enabled hardware
++static const struct iio_trigger_ops time_trigger_ops = {
++ .set_trigger_state = &bmi323_set_trigger_state,
++ //.reenable = NULL,
++ .validate_device = &iio_trigger_validate_own_device,
++};
++*/
++
++/*
++ * A very basic scan mask: everything can work in conjunction with everything else so no need to worry about
++ * managing conbinations of mutually exclusive data sources...
++ */
++static const unsigned long bmi323_accel_scan_masks[] = {
++ BIT(BMI323_ACCEL_AXIS_X) | BIT(BMI323_ACCEL_AXIS_Y) |
++ BIT(BMI323_ACCEL_AXIS_Z) | BIT(BMI323_GYRO_AXIS_X) |
++ BIT(BMI323_GYRO_AXIS_Y) |
++ BIT(BMI323_GYRO_AXIS_Z) /*| BIT(BMI323_TEMP)*/,
++ 0
++};
++
++int bmi323_iio_init(struct iio_dev *indio_dev)
++{
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++ struct irq_data *irq_desc = NULL;
++
++ if (data->bmi323.i2c_client != NULL) {
++ data->bmi323.dev = &data->bmi323.i2c_client->dev;
++ } else if (data->bmi323.spi_client != NULL) {
++ data->bmi323.dev = &data->bmi323.spi_client->dev;
++ } else {
++ return -ENODEV;
++ }
++
++ int ret = 0;
++
++ /* change to 8 for a default 200Hz sampling rate */
++ const int gyr_odr_conf_idx = 7;
++ const int acc_odr_conf_idx = 7;
++
++ mutex_init(&data->bmi323.mutex);
++
++ data->bmi323.acc_odr_time_ns =
++ bmi323_accel_odr_map[acc_odr_conf_idx].time_ns;
++ data->bmi323.gyr_odr_time_ns =
++ bmi323_gyro_odr_map[gyr_odr_conf_idx].time_ns;
++
++ // FIFO enabled for gyro, accel and temp. Overwrite older samples.
++ data->bmi323.fifo_conf_reg_value = cpu_to_le16((u16)0x0F00U);
++ //data->bmi323.fifo_conf_reg_value = cpu_to_le16((u16)0x0E00U);
++ //data->bmi323.fifo_conf_reg_value = cpu_to_le16((u16)0x0600U); // working
++
++ // now set the (default) normal mode...
++ // normal mode: 0x4000
++ // no averaging: 0x0000
++ data->bmi323.acc_conf_reg_value = cpu_to_le16(
++ 0x4000 | ((u16)BMC150_BMI323_ACCEL_RANGE_2_VAL << (u16)4U) |
++ ((u16)bmi323_accel_odr_map[acc_odr_conf_idx].hw_val));
++
++ // now set the (default) normal mode...
++ // normal mode: 0x4000
++ // no averaging: 0x0000
++ // filtering to ODR/2: 0x0000
++ data->bmi323.gyr_conf_reg_value = cpu_to_le16(
++ 0x4000 | ((u16)BMC150_BMI323_GYRO_RANGE_125_VAL << (u16)4U) |
++ ((u16)bmi323_gyro_odr_map[gyr_odr_conf_idx].hw_val));
++
++ // the datasheet states that FIFO buffer MUST be enabled before enabling any sensor
++ ret = bmi323_write_u16(&data->bmi323, BMC150_BMI323_FIFO_CONF_REG,
++ data->bmi323.fifo_conf_reg_value);
++ if (ret != 0) {
++ return -1;
++ }
++
++ ret = bmi323_write_u16(&data->bmi323, BMC150_BMI323_ACC_CONF_REG,
++ data->bmi323.acc_conf_reg_value);
++ if (ret != 0) {
++ return -1;
++ }
++
++ ret = bmi323_write_u16(&data->bmi323, BMC150_BMI323_GYR_CONF_REG,
++ data->bmi323.gyr_conf_reg_value);
++ if (ret != 0) {
++ return -2;
++ }
++
++ indio_dev->channels = bmi323_channels;
++ indio_dev->num_channels = ARRAY_SIZE(bmi323_channels);
++ indio_dev->name = "bmi323";
++ indio_dev->available_scan_masks = bmi323_accel_scan_masks;
++ indio_dev->modes = INDIO_DIRECT_MODE;
++ indio_dev->info = &bmi323_accel_info;
++ indio_dev->label = "bmi323-accel_base";
++
++ if (data->bmi323.irq > 0) {
++ dev_info(data->bmi323.dev, "IRQ pin reported as connected: %d",
++ data->bmi323.irq);
++
++ irq_desc = irq_get_irq_data(data->bmi323.irq);
++ if (!irq_desc) {
++ dev_err(data->bmi323.dev,
++ "Could not find IRQ %d. ignoring it.\n",
++ data->bmi323.irq);
++ goto bmi323_iio_init_missing_irq_pin;
++ }
++
++ //data->bmi323.trig[0] = devm_iio_trigger_alloc(data->bmi323.dev, "trig-fifo_full-%s-%d", indio_dev->name, iio_device_id(indio_dev));
++ //if (data->bmi323.trig[0] == NULL) {
++ // ret = -ENOMEM;
++ // goto bmi323_iio_init_err_trigger_unregister;
++ //}
++ //
++ //data->bmi323.trig[0]->ops = &time_trigger_ops;
++ //iio_trigger_set_drvdata(data->bmi323.trig[0], indio_dev);
++ //ret = devm_iio_trigger_register(data->bmi323.dev, data->bmi323.trig[0]);
++ //if (ret) {
++ // dev_err(data->bmi323.dev, "iio trigger register failed\n");
++ // goto bmi323_iio_init_err_trigger_unregister;
++ //}
++
++ /*
++ * register triggers BEFORE buffer setup so that they are cleared
++ * on emergence exit by bmi323_iio_init_err_trigger_unregister.
++ *
++ * This is just a placeholder until I can get my hands on a bmi323
++ * device that has the IRQ pin actually connected to the CPU.
++ */
++
++ /* here resume operation with the module part common to irq and non-irq enabled code. */
++ goto bmi323_iio_init_common_irq_and_not_irq;
++ }
++
++bmi323_iio_init_missing_irq_pin:
++ dev_info(
++ data->bmi323.dev,
++ "IRQ pin NOT connected (irq=%d). Will continue normally without triggers.",
++ data->bmi323.irq);
++
++bmi323_iio_init_common_irq_and_not_irq:
++
++ /* Once orientation matrix is implemented switch this to iio_triggered_buffer_setup_ext. */
++ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
++ iio_bmi323_trigger_h,
++ &bmi323_buffer_ops);
++ if (ret < 0) {
++ dev_err(data->bmi323.dev,
++ "Failed: iio triggered buffer setup: %d\n", ret);
++ goto bmi323_iio_init_err_trigger_unregister;
++ }
++
++ ret = pm_runtime_set_active(data->bmi323.dev);
++ if (ret) {
++ dev_err(data->bmi323.dev,
++ "bmi323 unable to initialize runtime PD: pm_runtime_set_active returned %d\n",
++ ret);
++ goto bmi323_iio_init_err_buffer_cleanup;
++ }
++
++ pm_runtime_enable(data->bmi323.dev);
++ pm_runtime_set_autosuspend_delay(data->bmi323.dev,
++ BMC150_BMI323_AUTO_SUSPEND_DELAY_MS);
++ pm_runtime_use_autosuspend(data->bmi323.dev);
++
++ ret = iio_device_register(indio_dev);
++ if (ret < 0) {
++ dev_err(data->bmi323.dev,
++ "bmi323 unable to register iio device: %d\n", ret);
++ goto bmi323_iio_init_err_pm_cleanup;
++ }
++
++ return 0;
++
++bmi323_iio_init_err_pm_cleanup:
++ pm_runtime_dont_use_autosuspend(data->bmi323.dev);
++ pm_runtime_disable(data->bmi323.dev);
++bmi323_iio_init_err_buffer_cleanup:
++ iio_triggered_buffer_cleanup(indio_dev);
++bmi323_iio_init_err_trigger_unregister:
++ /*
++ * unregister triggers if they have been setup already.
++ * iio_trigger_unregister shall be used in that regard.
++ *
++ * This is just a placeholder until I can get my hands on a bmi323
++ * device that has the IRQ pin actually connected to the CPU.
++ */
++ //if (data->bmi323.trig[0] != NULL) {
++ // iio_trigger_unregister(data->bmi323.trig[0]);
++ //}
++
++ return ret;
++}
++EXPORT_SYMBOL_NS_GPL(bmi323_iio_init, IIO_BMC150);
++
++void bmi323_iio_deinit(struct iio_dev *indio_dev)
++{
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++ struct device *dev = bmi323_get_managed_device(&data->bmi323);
++
++ iio_device_unregister(indio_dev);
++
++ pm_runtime_disable(dev);
++ pm_runtime_set_suspended(dev);
++ pm_runtime_put_noidle(dev);
++
++ iio_triggered_buffer_cleanup(indio_dev);
++
++ //iio_device_free(indio_dev); // this isn't done in the bmg160 driver nor in other drivers so I guess I shouldn't do it too
++
++ mutex_unlock(&data->bmi323.mutex);
++ bmi323_chip_rst(&data->bmi323);
++ mutex_unlock(&data->bmi323.mutex);
++}
++EXPORT_SYMBOL_NS_GPL(bmi323_iio_deinit, IIO_BMC150);
++
++#ifdef CONFIG_PM_SLEEP
++static int bmc150_accel_suspend(struct device *dev)
+ {
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
++ if (data->dev_type == BMI323) {
++ int ret;
++
++ //dev_warn(dev, "bmi323 suspending driver...");
++
++ // here push the register GYRO & ACCEL configuration and issue a reset so that chip goes to sleep mode (the default one after a reset)
++ mutex_unlock(&data->bmi323.mutex);
++
++ ret = bmi323_chip_rst(&data->bmi323);
++ mutex_unlock(&data->bmi323.mutex);
++ if (ret != 0) {
++ dev_err(dev,
++ "bmi323 error in suspend on bmi323_chip_rst: %d\n",
++ ret);
++ data->bmi323.flags |= BMI323_FLAGS_RESET_FAILED;
++ return -EAGAIN;
++ }
++
++ return 0;
++ }
++
+ mutex_lock(&data->mutex);
+ bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
+ mutex_unlock(&data->mutex);
+@@ -1844,6 +4005,63 @@ static int bmc150_accel_resume(struct device *dev)
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
++ if (data->dev_type == BMI323) {
++ int ret;
++
++ //dev_warn(dev, "bmi323 resuming driver...");
++
++ // here pop the register GYRO & ACCEL configuration and issue a reset so that chip goes to sleep mode (the default one after a reset)
++ mutex_lock(&data->bmi323.mutex);
++
++ // this was done already in runtime_sleep function.
++ if ((data->bmi323.flags & BMI323_FLAGS_RESET_FAILED) != 0x00U) {
++ ret = bmi323_chip_rst(&data->bmi323);
++ if (ret == 0) {
++ data->bmi323.flags &=
++ ~BMI323_FLAGS_RESET_FAILED;
++ } else {
++ goto bmi323_bmc150_accel_resume_terminate;
++ }
++ }
++
++ ret = bmi323_write_u16(&data->bmi323,
++ BMC150_BMI323_FIFO_CONF_REG,
++ data->bmi323.fifo_conf_reg_value);
++ if (ret != 0) {
++ goto bmi323_bmc150_accel_resume_terminate;
++ }
++
++ ret = bmi323_write_u16(&data->bmi323,
++ BMC150_BMI323_GYR_CONF_REG,
++ data->bmi323.gyr_conf_reg_value);
++ if (ret != 0) {
++ goto bmi323_bmc150_accel_resume_terminate;
++ }
++
++ ret = bmi323_write_u16(&data->bmi323,
++ BMC150_BMI323_ACC_CONF_REG,
++ data->bmi323.acc_conf_reg_value);
++ if (ret != 0) {
++ goto bmi323_bmc150_accel_resume_terminate;
++ }
++
++bmi323_bmc150_accel_resume_terminate:
++ mutex_unlock(&data->bmi323.mutex);
++ if (ret != 0) {
++ return -EAGAIN;
++ }
++
++ /*
++ * datasheet says "Start-up time": suspend to high performance mode is tipically 30ms,
++ * however when setting this to 32 or even higher the first reading from the gyro (unlike accel part)
++ * is actually the (wrong) default value 0x8000 so it is better to sleep a bit longer
++ * to prevent issues and give time to the sensor to pick up first readings...
++ */
++ msleep_interruptible(64);
++
++ return 0;
++ }
++
+ mutex_lock(&data->mutex);
+ bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
+ bmc150_accel_fifo_set_mode(data);
+@@ -1863,6 +4081,25 @@ static int bmc150_accel_runtime_suspend(struct device *dev)
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+ int ret;
+
++ if (data->dev_type == BMI323) {
++ //dev_warn(dev, "bmi323 suspending runtime...");
++
++ /*
++ * Every operation requiring this function have the mutex locked already:
++ * with mutex_lock(&data->bmi323.mutex);
++ */
++ ret = bmi323_chip_rst(&data->bmi323);
++ if (ret != 0) {
++ dev_err(dev,
++ "bmi323 error in runtime_suspend on bmi323_chip_rst: %d\n",
++ ret);
++ data->bmi323.flags |= BMI323_FLAGS_RESET_FAILED;
++ return -EAGAIN;
++ }
++
++ return 0;
++ }
++
+ ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
+ if (ret < 0)
+ return -EAGAIN;
+@@ -1877,6 +4114,70 @@ static int bmc150_accel_runtime_resume(struct device *dev)
+ int ret;
+ int sleep_val;
+
++ if (data->dev_type == BMI323) {
++ //dev_warn(dev, "bmi323 resuming runtime...");
++
++ /*
++ * Every operation requiring this function have the mutex locked already:
++ * with mutex_lock(&data->bmi323.mutex);
++ */
++
++ // recover from a bad state if it was left that way on reuntime_suspend
++ if ((data->bmi323.flags & BMI323_FLAGS_RESET_FAILED) != 0x00U) {
++ ret = bmi323_chip_rst(&data->bmi323);
++ if (ret == 0) {
++ data->bmi323.flags &=
++ ~BMI323_FLAGS_RESET_FAILED;
++ } else {
++ goto bmi323_bmc150_accel_runtime_resume_terminate;
++ }
++ }
++
++ ret = bmi323_write_u16(&data->bmi323,
++ BMC150_BMI323_FIFO_CONF_REG,
++ data->bmi323.fifo_conf_reg_value);
++ if (ret != 0) {
++ dev_err(dev,
++ "bmi323 writing to GYR_CONF register failed");
++ goto bmi323_bmc150_accel_runtime_resume_terminate;
++ }
++
++ ret = bmi323_write_u16(&data->bmi323,
++ BMC150_BMI323_GYR_CONF_REG,
++ data->bmi323.gyr_conf_reg_value);
++ if (ret != 0) {
++ dev_err(dev,
++ "bmi323 writing to GYR_CONF register failed");
++ goto bmi323_bmc150_accel_runtime_resume_terminate;
++ }
++
++ ret = bmi323_write_u16(&data->bmi323,
++ BMC150_BMI323_ACC_CONF_REG,
++ data->bmi323.acc_conf_reg_value);
++ if (ret != 0) {
++ dev_err(dev,
++ "bmi323 writing to ACC_CONF register failed");
++ goto bmi323_bmc150_accel_runtime_resume_terminate;
++ }
++
++bmi323_bmc150_accel_runtime_resume_terminate:
++ if (ret != 0) {
++ dev_err(dev,
++ "bmi323 bmc150_accel_runtime_resume -EAGAIN");
++ return -EAGAIN;
++ }
++
++ /*
++ * datasheet says "Start-up time": suspend to high performance mode is tipically 30ms,
++ * however when setting this to 32 or even higher the first reading from the gyro (unlike accel part)
++ * is actually the (wrong) default value 0x8000 so it is better to sleep a bit longer
++ * to prevent issues and give time to the sensor to pick up first readings...
++ */
++ msleep_interruptible(64);
++
++ return 0;
++ }
++
+ ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
+ if (ret < 0)
+ return ret;
+diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c
+index ee1ba134ad42..0d6ee304b3e7 100644
+--- a/drivers/iio/accel/bmc150-accel-i2c.c
++++ b/drivers/iio/accel/bmc150-accel-i2c.c
+@@ -173,15 +173,102 @@ static void bmc150_acpi_dual_accel_remove(struct i2c_client *client) {}
+
+ static int bmc150_accel_probe(struct i2c_client *client)
+ {
++ int ret;
++ u8 chip_id_first[4] = { 0x00, 0x00, 0x00, 0x00 };
++ enum bmc150_device_type dev_type = BMC150;
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct regmap *regmap;
+ const char *name = NULL;
+ enum bmc150_type type = BOSCH_UNKNOWN;
++
++ /* reads 4 bytes (2 dummy + 2 good) from the i2c CHIP_ID device register */
++ ret = i2c_smbus_read_i2c_block_data(client, 0x00, 4, &chip_id_first[0]);
++ if (ret != 4) {
++ dev_info(
++ &client->dev,
++ "error checking if the bmc150 is in fact a bmi323: i2c_smbus_read_i2c_block_data = %d: reg = 0x%02x.\n\tIt probably is a bmc150 as correctly reported by the ACPI entry.",
++ (int)ret, 0x00);
++ goto bmi150_old_probe;
++ }
++
++ // at this point we have enough data to know what chip we are handling
++ dev_type = (chip_id_first[2] == 0x43) ? BMI323 : dev_type;
++
++ if (dev_type == BMI323) {
++ dev_warn(
++ &client->dev,
++ "bmc323: what the ACPI table reported as a bmc150 is in fact a bmc323\n");
++
++ struct iio_dev *indio_dev = devm_iio_device_alloc(
++ &client->dev, sizeof(struct bmc150_accel_data));
++ if (!indio_dev) {
++ dev_err(&client->dev,
++ "bmc323 init process failed: out of memory\n");
++
++ return -ENOMEM;
++ }
++
++ dev_set_drvdata(&client->dev, indio_dev);
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++ data->dev_type = dev_type;
++
++ struct bmi323_private_data *bmi323_data = &data->bmi323;
++ bmi323_data->i2c_client = client;
++ bmi323_data->spi_client = NULL;
++ bmi323_data->irq = client->irq;
++
++ /*
++ * VDD is the analog and digital domain voltage supply
++ * VDDIO is the digital I/O voltage supply
++ */
++ bmi323_data->regulators[0].supply = "vdd";
++ bmi323_data->regulators[1].supply = "vddio";
++ ret = devm_regulator_bulk_get(
++ &client->dev, ARRAY_SIZE(bmi323_data->regulators),
++ bmi323_data->regulators);
++ if (ret) {
++ return dev_err_probe(&client->dev, ret,
++ "failed to get regulators\n");
++ }
++
++ ret = regulator_bulk_enable(ARRAY_SIZE(bmi323_data->regulators),
++ bmi323_data->regulators);
++ if (ret) {
++ iio_device_free(indio_dev);
++
++ dev_err(&client->dev,
++ "failed to enable regulators: %d\n", ret);
++ return ret;
++ }
++
++ ret = bmi323_chip_rst(bmi323_data);
++ if (ret != 0) {
++ dev_err(&client->dev,
++ "bmc323: error issuing the chip reset: %d\n",
++ ret);
++ return ret;
++ }
++
++ dev_info(
++ &client->dev,
++ "bmc323: chip reset success: starting the iio subsystem binding\n");
++
++ ret = bmi323_iio_init(indio_dev);
++ if (ret != 0) {
++ return ret;
++ }
++
++ return 0;
++ }
++
++bmi150_old_probe:
++ dev_info(&client->dev,
++ "executing the normal procedure for a bmc150...");
++
+ bool block_supported =
+ i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK);
+- int ret;
+
+ regmap = devm_regmap_init_i2c(client, &bmc150_regmap_conf);
+ if (IS_ERR(regmap)) {
+@@ -198,7 +285,7 @@ static int bmc150_accel_probe(struct i2c_client *client)
+ type, name, block_supported);
+ if (ret)
+ return ret;
+-
++
+ /*
+ * The !id check avoids recursion when probe() gets called
+ * for the second client.
+@@ -211,6 +298,15 @@ static int bmc150_accel_probe(struct i2c_client *client)
+
+ static void bmc150_accel_remove(struct i2c_client *client)
+ {
++ struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
++ struct bmc150_accel_data *data = iio_priv(indio_dev);
++
++ if (data->dev_type == BMI323) {
++ bmi323_iio_deinit(indio_dev);
++
++ return;
++ }
++
+ bmc150_acpi_dual_accel_remove(client);
+
+ bmc150_accel_core_remove(&client->dev);
+diff --git a/drivers/iio/accel/bmc150-accel.h b/drivers/iio/accel/bmc150-accel.h
+index 7775c5edaeef..65ec208960df 100644
+--- a/drivers/iio/accel/bmc150-accel.h
++++ b/drivers/iio/accel/bmc150-accel.h
+@@ -8,6 +8,14 @@
+ #include <linux/regulator/consumer.h>
+ #include <linux/workqueue.h>
+
++/*
++ * the bmi323 needs raw access to spi and i2c: I cannot use regmap
++ * as this device expects i2c writes to be 2 bytes,
++ * spi reads to be 3 bytes and i2c reads to be 4 bytes.
++ */
++#include <linux/i2c.h>
++#include <linux/spi/spi.h>
++
+ struct regmap;
+ struct i2c_client;
+ struct bmc150_accel_chip_info;
+@@ -34,6 +42,11 @@ struct bmc150_accel_interrupt {
+ atomic_t users;
+ };
+
++enum bmc150_device_type {
++ BMC150,
++ BMI323,
++};
++
+ struct bmc150_accel_trigger {
+ struct bmc150_accel_data *data;
+ struct iio_trigger *indio_trig;
+@@ -55,6 +68,25 @@ enum bmc150_accel_trigger_id {
+ BMC150_ACCEL_TRIGGERS,
+ };
+
++#define BMI323_FLAGS_RESET_FAILED 0x00000001U
++
++struct bmi323_private_data {
++ struct regulator_bulk_data regulators[2];
++ struct i2c_client *i2c_client;
++ struct spi_device *spi_client;
++ struct device *dev; /* pointer at i2c_client->dev or spi_client->dev */
++ struct mutex mutex;
++ int irq;
++ u32 flags;
++ u16 acc_conf_reg_value;
++ u16 gyr_conf_reg_value;
++ u16 fifo_conf_reg_value;
++ struct iio_trigger *trig[1];
++ s64 fifo_frame_time_diff_ns;
++ s64 acc_odr_time_ns;
++ s64 gyr_odr_time_ns;
++};
++
+ struct bmc150_accel_data {
+ struct regmap *regmap;
+ struct regulator_bulk_data regulators[2];
+@@ -83,7 +115,67 @@ struct bmc150_accel_data {
+ void (*resume_callback)(struct device *dev);
+ struct delayed_work resume_work;
+ struct iio_mount_matrix orientation;
+-};
++ enum bmc150_device_type dev_type;
++ struct bmi323_private_data bmi323;
++ };
++
++/**
++ * This function performs a write of a u16 little-endian (regardless of CPU architecture) integer
++ * to a device register. Returns 0 on success or an error code otherwise.
++ *
++ * PRE: in_value holds the data to be sent to the sensor, in little endian format even on big endian
++ * architectures.
++ *
++ * NOTE: bmi323->dev can be NULL (not yet initialized) when this function is called
++ * therefore it is not needed and is not used inside the function
++ *
++ * WARNING: this function does not lock any mutex and synchronization MUST be performed by the caller
++ */
++int bmi323_write_u16(struct bmi323_private_data *bmi323, u8 in_reg, u16 in_value);
++
++/**
++ * This function performs a read of "good" values from the bmi323 discarding what
++ * in the datasheet is described as "dummy data": additional useles bytes.
++ *
++ * PRE: bmi323 has been partially initialized: i2c_device and spi_devices MUST be set to either
++ * the correct value or NULL
++ *
++ * NOTE: bmi323->dev can be NULL (not yet initialized) when this function is called
++ * therefore it is not needed and is not used inside the function
++ *
++ * POST: on success out_value is written with data from the sensor, as it came out, so the
++ * content is little-endian even on big endian architectures
++ *
++ * WARNING: this function does not lock any mutex and synchronization MUST be performed by the caller
++ */
++int bmi323_read_u16(struct bmi323_private_data *bmi323, u8 in_reg, u16* out_value);
++
++int bmi323_chip_check(struct bmi323_private_data *bmi323);
++
++/**
++ * Reset the chip in a known state that is ready to accept commands, but is not configured therefore after calling this function
++ * it is required to load a new configuration to start data acquisition.
++ *
++ * PRE: bmi323 has been fully identified and partially initialized
++ *
++ * NOTE: after issuing a reset the the chip will be in what it is called "suspended mode" and the feature angine is
++ * ready to be set. This mode has everything disabled and consumes aroud 15uA.
++ *
++ * When removing the driver or suspend has been requested it's best to reset the chip so that power consumption
++ * will be the lowest possible.
++ */
++int bmi323_chip_rst(struct bmi323_private_data *bmi323);
++
++/**
++ * This function MUST be called in probe and is responsible for registering the userspace sysfs.
++ *
++ * The indio_dev MUST have been allocated but not registered. This function will perform userspace registration.
++ *
++ * @param indio_dev the industrual io device already allocated but not yet registered
++ */
++int bmi323_iio_init(struct iio_dev *indio_dev);
++
++void bmi323_iio_deinit(struct iio_dev *indio_dev);
+
+ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
+ enum bmc150_type type, const char *name,
+--
+2.42.0
+
diff --git a/SOURCES/steam-deck.patch b/SOURCES/steam-deck.patch
index 395cdd3..9eba750 100644
--- a/SOURCES/steam-deck.patch
+++ b/SOURCES/steam-deck.patch
@@ -24,7 +24,7 @@ index 8b93856de432..af335d9150e9 100644
@@ -2260,5 +2260,16 @@ config MFD_RSMU_SPI
Additional drivers must be enabled in order to use the functionality
of the device.
-
+
+config MFD_STEAMDECK
+ tristate "Valve Steam Deck"
+ select MFD_CORE
@@ -206,7 +206,7 @@ index 7ac3daaf59ce..d784c78417cf 100644
@@ -1900,6 +1900,17 @@ config SENSORS_SCH5636
This driver can also be built as a module. If so, the module
will be called sch5636.
-
+
+config SENSORS_STEAMDECK
+ tristate "Steam Deck EC sensors"
+ depends on MFD_STEAMDECK
@@ -486,7 +486,7 @@ index 499d0f215a8b..d1d761695cd6 100644
@@ -864,6 +864,13 @@ config LEDS_ACER_A500
This option enables support for the Power Button LED of
Acer Iconia Tab A500.
-
+
+config LEDS_STEAMDECK
+ tristate "LED support for Steam Deck"
+ depends on LEDS_CLASS && MFD_STEAMDECK
@@ -495,7 +495,7 @@ index 499d0f215a8b..d1d761695cd6 100644
+ power button) on Steam Deck
+
source "drivers/leds/blink/Kconfig"
-
+
comment "Flash and Torch LED drivers"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4fd2f92cd198..130a1c175dde 100644
@@ -612,7 +612,7 @@ index 290186e44e6b..4d444a9e2c1f 100644
@@ -189,4 +189,11 @@ config EXTCON_USBC_TUSB320
Say Y here to enable support for USB Type C cable detection extcon
support using a TUSB320.
-
+
+config EXTCON_STEAMDECK
+ tristate "Steam Deck extcon support"
+ depends on MFD_STEAMDECK
@@ -840,7 +840,7 @@ index fab9e9460bd4..9d0a5471b181 100644
@@ -180,6 +180,76 @@ static const struct hwmon_chip_info steamdeck_hwmon_chip_info = {
.info = steamdeck_hwmon_info,
};
-
+
+
+static ssize_t
+steamdeck_hwmon_simple_store(struct device *dev, const char *buf, size_t count,
@@ -948,7 +948,7 @@ index 0e504b3c2796..a60fa7db9141 100644
@@ -41,9 +41,29 @@ struct steamdeck {
STEAMDECK_ATTR_RO(firmware_version, "PDFW");
STEAMDECK_ATTR_RO(board_id, "BOID");
-
+
+static ssize_t controller_board_power_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
@@ -1010,13 +1010,13 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
#include <linux/sched.h>
#include <linux/usb/g_hid.h>
+#include <uapi/linux/usb/g_hid.h>
-
+
#include "u_f.h"
#include "u_hid.h"
@@ -75,6 +76,13 @@ struct f_hidg {
wait_queue_head_t write_queue;
struct usb_request *req;
-
+
+ /* get report */
+ struct usb_request *get_req;
+ struct usb_hidg_report get_report;
@@ -1030,7 +1030,7 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
@@ -523,6 +531,64 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
return status;
}
-
+
+
+static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer)
+{
@@ -1097,13 +1097,13 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
#undef READ_COND_SSREPORT
#undef READ_COND_INTOUT
+#undef GET_REPORT_COND
-
+
static int f_hidg_release(struct inode *inode, struct file *fd)
{
@@ -640,6 +707,10 @@ static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req)
wake_up(&hidg->read_queue);
}
-
+
+static void hidg_get_report_complete(struct usb_ep *ep, struct usb_request *req)
+{
+}
@@ -1118,7 +1118,7 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
+ unsigned long flags;
+ bool do_wake = false;
__u16 value, length;
-
+
value = __le16_to_cpu(ctrl->wValue);
@@ -659,14 +732,29 @@ static int hidg_setup(struct usb_function *f,
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
@@ -1126,7 +1126,7 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
| HID_REQ_GET_REPORT):
- VDBG(cdev, "get_report\n");
+ VDBG(cdev, "get_report | wLength=%d\n", ctrl->wLength);
-
+
- /* send an empty report */
- length = min_t(unsigned, length, hidg->report_length);
- memset(req->buf, 0x0, length);
@@ -1137,7 +1137,7 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
+ if (status < 0) {
+ ERROR(cdev, "usb_ep_queue error on get_report %d\n",
+ status);
-
+
- goto respond;
- break;
+ spin_lock_irqsave(&hidg->get_spinlock, flags);
@@ -1153,11 +1153,11 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
+ }
+
+ return status;
-
+
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_PROTOCOL):
@@ -800,6 +888,14 @@ static void hidg_disable(struct usb_function *f)
-
+
hidg->req = NULL;
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
@@ -1169,7 +1169,7 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
+ hidg->get_req = NULL;
+ spin_unlock_irqrestore(&hidg->get_spinlock, flags);
}
-
+
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
@@ -908,6 +1004,7 @@ static const struct file_operations f_hidg_fops = {
.write = f_hidg_write,
@@ -1178,11 +1178,11 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
+ .unlocked_ioctl = f_hidg_ioctl,
.llseek = noop_llseek,
};
-
+
@@ -918,6 +1015,14 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_string *us;
int status;
-
+
+ hidg->get_req = usb_ep_alloc_request(c->cdev->gadget->ep0, GFP_ATOMIC);
+ if (!hidg->get_req)
+ return -ENOMEM;
@@ -1203,17 +1203,17 @@ index ea85e2c701a15..6fec92b5a0bd9 100644
init_waitqueue_head(&hidg->read_queue);
+ init_waitqueue_head(&hidg->get_queue);
INIT_LIST_HEAD(&hidg->completed_out_req);
-
+
/* create char device */
@@ -1021,6 +1128,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (hidg->req != NULL)
free_ep_req(hidg->in_ep, hidg->req);
-
+
+ usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req);
+
return status;
}
-
+
diff --git a/include/uapi/linux/usb/g_hid.h b/include/uapi/linux/usb/g_hid.h
new file mode 100644
index 0000000000000..c6068b4863543
@@ -1264,14 +1264,14 @@ index 835473910a498..9754822b2a409 100644
+++ b/include/uapi/linux/usb/gadgetfs.h
@@ -62,7 +62,7 @@ struct usb_gadgetfs_event {
};
-
-
+
+
-/* The 'g' code is also used by printer gadget ioctl requests.
+/* The 'g' code is also used by printer and hid gadget ioctl requests.
* Don't add any colliding codes to either driver, and keep
* them in unique ranges (size 0x20 for now).
*/
---
+--
2.41.0
@@ -1306,7 +1306,7 @@ index 6fec92b5a0bd9..172cba91aded1 100644
@@ -76,6 +76,11 @@ struct f_hidg {
wait_queue_head_t write_queue;
struct usb_request *req;
-
+
+ /* set report */
+ struct list_head completed_set_req;
+ spinlock_t set_spinlock;
@@ -1318,7 +1318,7 @@ index 6fec92b5a0bd9..172cba91aded1 100644
@@ -531,6 +536,54 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
return status;
}
-
+
+static int f_hidg_set_report(struct file *file, struct usb_hidg_report __user *buffer)
+{
+ struct f_hidg *hidg = file->private_data;
@@ -1367,7 +1367,7 @@ index 6fec92b5a0bd9..172cba91aded1 100644
+ free_ep_req(hidg->func.config->cdev->gadget->ep0, req);
+ return status;
+}
-
+
static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer)
{
@@ -582,6 +635,8 @@ static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *b
@@ -1380,34 +1380,34 @@ index 6fec92b5a0bd9..172cba91aded1 100644
return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg);
default:
@@ -596,6 +651,7 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
-
+
poll_wait(file, &hidg->read_queue, wait);
poll_wait(file, &hidg->write_queue, wait);
+ poll_wait(file, &hidg->set_queue, wait);
-
+
if (WRITE_COND)
ret |= EPOLLOUT | EPOLLWRNORM;
@@ -608,12 +664,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
ret |= EPOLLIN | EPOLLRDNORM;
}
-
+
+ if (SET_REPORT_COND)
+ ret |= EPOLLPRI;
+
return ret;
}
-
+
#undef WRITE_COND
#undef READ_COND_SSREPORT
#undef READ_COND_INTOUT
+#undef SET_REPORT_COND
#undef GET_REPORT_COND
-
+
static int f_hidg_release(struct inode *inode, struct file *fd)
@@ -658,11 +718,19 @@ static void hidg_intout_complete(struct usb_ep *ep, struct usb_request *req)
-
+
req_list->req = req;
-
+
- spin_lock_irqsave(&hidg->read_spinlock, flags);
- list_add_tail(&req_list->list, &hidg->completed_out_req);
- spin_unlock_irqrestore(&hidg->read_spinlock, flags);
@@ -1415,7 +1415,7 @@ index 6fec92b5a0bd9..172cba91aded1 100644
+ spin_lock_irqsave(&hidg->set_spinlock, flags);
+ list_add_tail(&req_list->list, &hidg->completed_set_req);
+ spin_unlock_irqrestore(&hidg->set_spinlock, flags);
-
+
- wake_up(&hidg->read_queue);
+ wake_up(&hidg->set_queue);
+ } else {
@@ -1457,13 +1457,13 @@ index 6fec92b5a0bd9..172cba91aded1 100644
+ }
+
+ return status;
-
+
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_SET_PROTOCOL):
@@ -880,6 +963,14 @@ static void hidg_disable(struct usb_function *f)
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
}
-
+
+ spin_lock_irqsave(&hidg->set_spinlock, flags);
+ list_for_each_entry_safe(list, next, &hidg->completed_set_req, list) {
+ free_ep_req(f->config->cdev->gadget->ep0, list->req);
@@ -1487,7 +1487,7 @@ index 6fec92b5a0bd9..172cba91aded1 100644
init_waitqueue_head(&hidg->get_queue);
INIT_LIST_HEAD(&hidg->completed_out_req);
+ INIT_LIST_HEAD(&hidg->completed_set_req);
-
+
/* create char device */
cdev_init(&hidg->cdev, &f_hidg_fops);
diff --git a/include/uapi/linux/usb/g_hid.h b/include/uapi/linux/usb/g_hid.h
@@ -1515,12 +1515,12 @@ index c6068b4863543..54814c2c68d60 100644
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
+
#ifndef __UAPI_LINUX_USB_G_HID_H
#define __UAPI_LINUX_USB_G_HID_H
-
+
#include <linux/types.h>
-
+
+#define HIDG_REPORT_SIZE_MAX 64
+
struct usb_hidg_report {
@@ -1528,16 +1528,16 @@ index c6068b4863543..54814c2c68d60 100644
- __u8 data[512];
+ __u8 data[HIDG_REPORT_SIZE_MAX];
};
-
+
/* The 'g' code is also used by gadgetfs and hid gadget ioctl requests.
* Don't add any colliding codes to either driver, and keep
* them in unique ranges (size 0x20 for now).
*/
+#define GADGET_HID_READ_SET_REPORT _IOR('g', 0x41, struct usb_hidg_report)
#define GADGET_HID_WRITE_GET_REPORT _IOW('g', 0x42, struct usb_hidg_report)
-
+
#endif /* __UAPI_LINUX_USB_G_HID_H */
---
+--
2.41.0
@@ -1562,7 +1562,7 @@ index b110818fc9458..39a9bf3b7f77d 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -71,7 +71,7 @@ static LIST_HEAD(steam_devices);
-
+
/*
* Commands that can be sent in a feature report.
- * Thanks to Valve for some valuable hints.
@@ -1618,7 +1618,7 @@ index b110818fc9458..39a9bf3b7f77d 100644
+#define STEAM_CMD_DONGLE_GET_CONN_SLOTS 0xc4
+#define STEAM_CMD_HAPTIC_CMD 0xea
#define STEAM_CMD_HAPTIC_RUMBLE 0xeb
-
+
/* Some useful register ids */
-#define STEAM_REG_LPAD_MODE 0x07
-#define STEAM_REG_RPAD_MODE 0x08
@@ -1679,13 +1679,13 @@ index b110818fc9458..39a9bf3b7f77d 100644
+#define STEAM_REG_SLEEP_INACTIVITY_TIMEOUT 0x32
+#define STEAM_REG_LEFT_TRACKPAD_CLICK_PRESSURE 0x34
+#define STEAM_REG_RIGHT_TRACKPAD_CLICK_PRESSURE 0x35
-
+
/* Raw event identifiers */
#define STEAM_EV_INPUT_DATA 0x01
@@ -108,13 +179,28 @@ static LIST_HEAD(steam_devices);
#define STEAM_EV_BATTERY 0x04
#define STEAM_EV_DECK_INPUT_DATA 0x09
-
+
+/* String attribute idenitifiers */
+#define STEAM_ATTRIB_STR_BOARD_SERIAL 0x00
+#define STEAM_ATTRIB_STR_UNIT_SERIAL 0x01
@@ -1714,7 +1714,7 @@ index b110818fc9458..39a9bf3b7f77d 100644
+#define STEAM_TRACKPAD_ABSOLUTE_DPAD 0x06
+#define STEAM_TRACKPAD_NONE 0x07
+#define STEAM_TRACKPAD_GESTURE_KEYBOARD 0x08
-
+
/* Other random constants */
#define STEAM_SERIAL_LEN 10
@@ -232,7 +318,7 @@ static int steam_write_registers(struct steam_device *steam,
@@ -1725,7 +1725,7 @@ index b110818fc9458..39a9bf3b7f77d 100644
+ u8 cmd[64] = {STEAM_CMD_SET_REGISTER, 0x00};
int ret;
va_list args;
-
+
@@ -268,7 +354,7 @@ static int steam_get_serial(struct steam_device *steam)
* Recv: 0xae 0x15 0x01 serialnumber (10 chars)
*/
@@ -1733,7 +1733,7 @@ index b110818fc9458..39a9bf3b7f77d 100644
- u8 cmd[] = {STEAM_CMD_GET_SERIAL, 0x15, 0x01};
+ u8 cmd[] = {STEAM_CMD_GET_STRING_ATTRIB, 0x15, STEAM_ATTRIB_STR_UNIT_SERIAL};
u8 reply[3 + STEAM_SERIAL_LEN + 1];
-
+
ret = steam_send_report(steam, cmd, sizeof(cmd));
@@ -277,7 +363,7 @@ static int steam_get_serial(struct steam_device *steam)
ret = steam_recv_report(steam, reply, sizeof(reply));
@@ -1751,7 +1751,7 @@ index b110818fc9458..39a9bf3b7f77d 100644
- return steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS);
+ return steam_send_report_byte(steam, STEAM_CMD_DONGLE_GET_STATE);
}
-
+
static inline int steam_haptic_rumble(struct steam_device *steam,
@@ -339,9 +425,9 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
/* enable esc, enter, cursors */
@@ -1763,10 +1763,10 @@ index b110818fc9458..39a9bf3b7f77d 100644
- STEAM_REG_RPAD_MARGIN, 0x01, /* enable margin */
+ STEAM_REG_SMOOTH_ABSOLUTE_MOUSE, 0x01, /* enable smooth */
0);
-
+
cancel_delayed_work_sync(&steam->heartbeat);
@@ -351,11 +437,11 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
-
+
if (steam->quirks & STEAM_QUIRK_DECK) {
steam_write_registers(steam,
- STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */
@@ -1804,7 +1804,7 @@ index b110818fc9458..39a9bf3b7f77d 100644
0);
schedule_delayed_work(&steam->heartbeat, 5 * HZ);
}
---
+--
2.41.0
@@ -1826,7 +1826,7 @@ index 39a9bf3b7f77d..0620046b142ef 100644
@@ -202,6 +202,11 @@ static LIST_HEAD(steam_devices);
#define STEAM_TRACKPAD_NONE 0x07
#define STEAM_TRACKPAD_GESTURE_KEYBOARD 0x08
-
+
+/* Pad identifiers for the deck */
+#define STEAM_PAD_LEFT 0
+#define STEAM_PAD_RIGHT 1
@@ -1834,7 +1834,7 @@ index 39a9bf3b7f77d..0620046b142ef 100644
+
/* Other random constants */
#define STEAM_SERIAL_LEN 10
-
+
@@ -221,6 +226,9 @@ struct steam_device {
u8 battery_charge;
u16 voltage;
@@ -1848,7 +1848,7 @@ index 39a9bf3b7f77d..0620046b142ef 100644
@@ -380,6 +388,33 @@ static inline int steam_request_conn_status(struct steam_device *steam)
return steam_send_report_byte(steam, STEAM_CMD_DONGLE_GET_STATE);
}
-
+
+/*
+ * Send a haptic pulse to the trackpads
+ * Duration and interval are measured in microseconds, count is the number
@@ -1880,7 +1880,7 @@ index 39a9bf3b7f77d..0620046b142ef 100644
u16 intensity, u16 left_speed, u16 right_speed,
u8 left_gain, u8 right_gain)
@@ -421,6 +456,9 @@ static int steam_play_effect(struct input_dev *dev, void *data,
-
+
static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
{
+ if (steam->gamepad_mode)
@@ -1892,7 +1892,7 @@ index 39a9bf3b7f77d..0620046b142ef 100644
@@ -805,6 +843,29 @@ static void steam_work_connect_cb(struct work_struct *work)
}
}
-
+
+static void steam_mode_switch_cb(struct work_struct *work)
+{
+ struct steam_device *steam = container_of(to_delayed_work(work),
@@ -1945,7 +1945,7 @@ index 39a9bf3b7f77d..0620046b142ef 100644
}
@@ -1393,6 +1457,14 @@ static void steam_do_deck_input_event(struct steam_device *steam,
input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2)));
-
+
input_sync(input);
+
+ if (!(b9 & BIT(6)) && steam->did_mode_switch) {
@@ -1956,9 +1956,9 @@ index 39a9bf3b7f77d..0620046b142ef 100644
+ schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100);
+ }
}
-
+
/*
---
+--
2.41.0
@@ -2000,7 +2000,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
+ int ret = 0;
u8 cmd[] = {STEAM_CMD_GET_STRING_ATTRIB, 0x15, STEAM_ATTRIB_STR_UNIT_SERIAL};
u8 reply[3 + STEAM_SERIAL_LEN + 1];
-
+
+ mutex_lock(&steam->report_mutex);
ret = steam_send_report(steam, cmd, sizeof(cmd));
if (ret < 0)
@@ -2023,7 +2023,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
+ mutex_unlock(&steam->report_mutex);
+ return ret;
}
-
+
/*
@@ -385,7 +390,11 @@ static int steam_get_serial(struct steam_device *steam)
*/
@@ -2036,7 +2036,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
+ mutex_unlock(&steam->report_mutex);
+ return ret;
}
-
+
/*
@@ -397,6 +406,7 @@ static inline int steam_request_conn_status(struct steam_device *steam)
static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad,
@@ -2044,42 +2044,42 @@ index 0620046b142ef..845ca71b8bd3a 100644
{
+ int ret;
u8 report[10] = {STEAM_CMD_HAPTIC_PULSE, 8};
-
+
/* Left and right are swapped on this report for legacy reasons */
@@ -412,13 +422,17 @@ static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad,
report[8] = count >> 8;
report[9] = gain;
-
+
- return steam_send_report(steam, report, sizeof(report));
+ mutex_lock(&steam->report_mutex);
+ ret = steam_send_report(steam, report, sizeof(report));
+ mutex_unlock(&steam->report_mutex);
+ return ret;
}
-
+
static inline int steam_haptic_rumble(struct steam_device *steam,
u16 intensity, u16 left_speed, u16 right_speed,
u8 left_gain, u8 right_gain)
{
+ int ret;
u8 report[11] = {STEAM_CMD_HAPTIC_RUMBLE, 9};
-
+
report[3] = intensity & 0xFF;
@@ -430,7 +444,10 @@ static inline int steam_haptic_rumble(struct steam_device *steam,
report[9] = left_gain;
report[10] = right_gain;
-
+
- return steam_send_report(steam, report, sizeof(report));
+ mutex_lock(&steam->report_mutex);
+ ret = steam_send_report(steam, report, sizeof(report));
+ mutex_unlock(&steam->report_mutex);
+ return ret;
}
-
+
static void steam_haptic_rumble_cb(struct work_struct *work)
@@ -460,6 +477,7 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
enable = false;
-
+
if (enable) {
+ mutex_lock(&steam->report_mutex);
/* enable esc, enter, cursors */
@@ -2090,13 +2090,13 @@ index 0620046b142ef..845ca71b8bd3a 100644
STEAM_REG_SMOOTH_ABSOLUTE_MOUSE, 0x01, /* enable smooth */
0);
+ mutex_unlock(&steam->report_mutex);
-
+
cancel_delayed_work_sync(&steam->heartbeat);
} else {
+ mutex_lock(&steam->report_mutex);
/* disable esc, enter, cursor */
steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS);
-
+
@@ -481,18 +501,19 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
STEAM_REG_LEFT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
STEAM_REG_RIGHT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
@@ -2125,7 +2125,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
struct steam_device *steam = input_get_drvdata(dev);
+ unsigned long flags;
+ bool set_lizard_mode;
-
+
- mutex_lock(&steam->mutex);
- if (!steam->client_opened && lizard_mode)
+ spin_lock_irqsave(&steam->lock, flags);
@@ -2137,13 +2137,13 @@ index 0620046b142ef..845ca71b8bd3a 100644
+
return 0;
}
-
+
static void steam_input_close(struct input_dev *dev)
{
struct steam_device *steam = input_get_drvdata(dev);
+ unsigned long flags;
+ bool set_lizard_mode;
-
+
- mutex_lock(&steam->mutex);
- if (!steam->client_opened && lizard_mode)
+ spin_lock_irqsave(&steam->lock, flags);
@@ -2153,14 +2153,14 @@ index 0620046b142ef..845ca71b8bd3a 100644
steam_set_lizard_mode(steam, true);
- mutex_unlock(&steam->mutex);
}
-
+
static enum power_supply_property steam_battery_props[] = {
@@ -760,6 +788,7 @@ static int steam_register(struct steam_device *steam)
{
int ret;
bool client_opened;
+ unsigned long flags;
-
+
/*
* This function can be called several times in a row with the
@@ -772,11 +801,9 @@ static int steam_register(struct steam_device *steam)
@@ -2172,13 +2172,13 @@ index 0620046b142ef..845ca71b8bd3a 100644
strscpy(steam->serial_no, "XXXXXXXXXX",
sizeof(steam->serial_no));
- mutex_unlock(&steam->mutex);
-
+
hid_info(steam->hdev, "Steam Controller '%s' connected",
steam->serial_no);
@@ -791,11 +818,11 @@ static int steam_register(struct steam_device *steam)
mutex_unlock(&steam_devices_lock);
}
-
+
- mutex_lock(&steam->mutex);
+ spin_lock_irqsave(&steam->lock, flags);
client_opened = steam->client_opened;
@@ -2186,7 +2186,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
if (!client_opened)
steam_set_lizard_mode(steam, lizard_mode);
- mutex_unlock(&steam->mutex);
-
+
if (!client_opened)
ret = steam_input_register(steam);
@@ -847,16 +874,21 @@ static void steam_mode_switch_cb(struct work_struct *work)
@@ -2198,7 +2198,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
steam->gamepad_mode = !steam->gamepad_mode;
if (!lizard_mode)
return;
-
+
- mutex_lock(&steam->mutex);
if (steam->gamepad_mode)
steam_set_lizard_mode(steam, false);
@@ -2212,7 +2212,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
+ if (!client_opened)
+ steam_set_lizard_mode(steam, lizard_mode);
+ }
-
+
steam_haptic_pulse(steam, STEAM_PAD_RIGHT, 0x190, 0, 1, 0);
if (steam->gamepad_mode) {
@@ -889,16 +921,21 @@ static void steam_lizard_mode_heartbeat(struct work_struct *work)
@@ -2221,7 +2221,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
heartbeat.work);
+ bool client_opened;
+ unsigned long flags;
-
+
- mutex_lock(&steam->mutex);
- if (!steam->client_opened && steam->client_hdev) {
+ spin_lock_irqsave(&steam->lock, flags);
@@ -2238,37 +2238,37 @@ index 0620046b142ef..845ca71b8bd3a 100644
}
- mutex_unlock(&steam->mutex);
}
-
+
static int steam_client_ll_parse(struct hid_device *hdev)
@@ -921,10 +958,11 @@ static void steam_client_ll_stop(struct hid_device *hdev)
static int steam_client_ll_open(struct hid_device *hdev)
{
struct steam_device *steam = hdev->driver_data;
+ unsigned long flags;
-
+
- mutex_lock(&steam->mutex);
+ spin_lock_irqsave(&steam->lock, flags);
steam->client_opened = true;
- mutex_unlock(&steam->mutex);
+ spin_unlock_irqrestore(&steam->lock, flags);
-
+
steam_input_unregister(steam);
-
+
@@ -939,14 +977,12 @@ static void steam_client_ll_close(struct hid_device *hdev)
bool connected;
-
+
spin_lock_irqsave(&steam->lock, flags);
- connected = steam->connected;
+ steam->client_opened = false;
+ connected = steam->connected && !steam->client_opened;
spin_unlock_irqrestore(&steam->lock, flags);
-
+
- mutex_lock(&steam->mutex);
- steam->client_opened = false;
if (connected)
steam_set_lizard_mode(steam, lizard_mode);
- mutex_unlock(&steam->mutex);
-
+
if (connected)
steam_input_register(steam);
@@ -1035,7 +1071,7 @@ static int steam_probe(struct hid_device *hdev,
@@ -2283,7 +2283,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
@@ -1043,13 +1079,6 @@ static int steam_probe(struct hid_device *hdev,
INIT_DEFERRABLE_WORK(&steam->heartbeat, steam_lizard_mode_heartbeat);
INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
-
+
- steam->client_hdev = steam_create_client_hid(hdev);
- if (IS_ERR(steam->client_hdev)) {
- ret = PTR_ERR(steam->client_hdev);
@@ -2297,7 +2297,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
@@ -1058,10 +1087,6 @@ static int steam_probe(struct hid_device *hdev,
if (ret)
goto hid_hw_start_fail;
-
+
- ret = hid_add_device(steam->client_hdev);
- if (ret)
- goto client_hdev_add_fail;
@@ -2308,7 +2308,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
@@ -1087,15 +1112,26 @@ static int steam_probe(struct hid_device *hdev,
}
}
-
+
+ steam->client_hdev = steam_create_client_hid(hdev);
+ if (IS_ERR(steam->client_hdev)) {
+ ret = PTR_ERR(steam->client_hdev);
@@ -2321,7 +2321,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
+ goto client_hdev_add_fail;
+
return 0;
-
+
-input_register_fail:
-hid_hw_open_fail:
client_hdev_add_fail:
@@ -2339,7 +2339,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
@@ -1115,14 +1151,12 @@ static void steam_remove(struct hid_device *hdev)
return;
}
-
+
+ cancel_delayed_work_sync(&steam->heartbeat);
+ cancel_delayed_work_sync(&steam->mode_switch);
+ cancel_work_sync(&steam->work_connect);
@@ -2355,7 +2355,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
hid_info(hdev, "Steam wireless receiver disconnected");
}
@@ -1597,10 +1631,8 @@ static int steam_param_set_lizard_mode(const char *val,
-
+
mutex_lock(&steam_devices_lock);
list_for_each_entry(steam, &steam_devices, list) {
- mutex_lock(&steam->mutex);
@@ -2365,7 +2365,7 @@ index 0620046b142ef..845ca71b8bd3a 100644
}
mutex_unlock(&steam_devices_lock);
return 0;
---
+--
2.41.0
@@ -2405,26 +2405,26 @@ index 845ca71b8bd3a..0c2fe51b29bc1 100644
- bool client_opened;
+ unsigned long client_opened;
unsigned long flags;
-
+
/*
@@ -961,7 +961,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
unsigned long flags;
-
+
spin_lock_irqsave(&steam->lock, flags);
- steam->client_opened = true;
+ steam->client_opened++;
spin_unlock_irqrestore(&steam->lock, flags);
-
+
steam_input_unregister(steam);
@@ -977,7 +977,7 @@ static void steam_client_ll_close(struct hid_device *hdev)
bool connected;
-
+
spin_lock_irqsave(&steam->lock, flags);
- steam->client_opened = false;
+ steam->client_opened--;
connected = steam->connected && !steam->client_opened;
spin_unlock_irqrestore(&steam->lock, flags);
-
+
@@ -1156,7 +1156,7 @@ static void steam_remove(struct hid_device *hdev)
cancel_work_sync(&steam->work_connect);
hid_destroy_device(steam->client_hdev);
@@ -2434,7 +2434,7 @@ index 845ca71b8bd3a..0c2fe51b29bc1 100644
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
hid_info(hdev, "Steam wireless receiver disconnected");
}
---
+--
2.41.0
@@ -2458,11 +2458,11 @@ index 0c2fe51b29bc1..92e3e1052fa42 100644
+++ b/drivers/hid/hid-steam.c
@@ -208,7 +208,7 @@ static LIST_HEAD(steam_devices);
#define STEAM_PAD_BOTH 2
-
+
/* Other random constants */
-#define STEAM_SERIAL_LEN 10
+#define STEAM_SERIAL_LEN 0x15
-
+
struct steam_device {
struct list_head list;
@@ -359,10 +359,10 @@ static int steam_get_serial(struct steam_device *steam)
@@ -2476,7 +2476,7 @@ index 0c2fe51b29bc1..92e3e1052fa42 100644
- u8 cmd[] = {STEAM_CMD_GET_STRING_ATTRIB, 0x15, STEAM_ATTRIB_STR_UNIT_SERIAL};
+ u8 cmd[] = {STEAM_CMD_GET_STRING_ATTRIB, sizeof(steam->serial_no), STEAM_ATTRIB_STR_UNIT_SERIAL};
u8 reply[3 + STEAM_SERIAL_LEN + 1];
-
+
mutex_lock(&steam->report_mutex);
@@ -372,12 +372,12 @@ static int steam_get_serial(struct steam_device *steam)
ret = steam_recv_report(steam, reply, sizeof(reply));
@@ -2493,5 +2493,5 @@ index 0c2fe51b29bc1..92e3e1052fa42 100644
out:
mutex_unlock(&steam->report_mutex);
return ret;
---
+--
2.41.0
diff --git a/SOURCES/steamdeck-oled-audio.patch b/SOURCES/steamdeck-oled-audio.patch
new file mode 100644
index 0000000..978e76a
--- /dev/null
+++ b/SOURCES/steamdeck-oled-audio.patch
@@ -0,0 +1,437 @@
+From ba5fcc25b726559cb36d61196c58c865190a8a7b Mon Sep 17 00:00:00 2001
+From c0c65da7ca4b0a4f93272f5e932dd7eed90703c8 Mon Sep 17 00:00:00 2001
+From: Ethan Geller <ethang@valvesoftware.com>
+Date: Thu, 16 Nov 2023 21:31:48 +0000
+Subject: [PATCH] Fix for Max98388 issue where speakers would not be powered on
+ when we resume from S3.
+
+The issue was that when we flush the regmap to the amp on resume, we were also flushing a value of 1 to the SW_RESET pin.
+
+Theoretically, this is fixed by marking the SW_RESET register volatile. However, we did not observe a fix after marking the register volatile, so we opted for a more complete fix of ensuring SW_RESET is zero in our regmap after we have waited for the reset loop to be complete.
+---
+ sound/soc/codecs/max98388.c | 24 ++++++++++++++++++++++--
+ 1 file changed, 22 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c
+index 293ec172e0a71..4c495e0e33f90 100644
+--- a/sound/soc/codecs/max98388.c
++++ b/sound/soc/codecs/max98388.c
+@@ -391,27 +391,43 @@ void max98388_reset(struct max98388_priv *max98388, struct device *dev)
+ {
+ int ret, reg, count;
+
++
+ /* Software Reset */
+ ret = regmap_update_bits(max98388->regmap,
+ MAX98388_R2000_SW_RESET,
+ MAX98388_SOFT_RESET,
+ MAX98388_SOFT_RESET);
+- if (ret)
++
++ if (ret) {
+ dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
++ goto exit;
++ }
++
+
+ count = 0;
+ while (count < 3) {
+ usleep_range(10000, 11000);
++
+ /* Software Reset Verification */
+ ret = regmap_read(max98388->regmap,
+ MAX98388_R22FF_REV_ID, &reg);
++
+ if (!ret) {
+ dev_info(dev, "Reset completed (retry:%d)\n", count);
+- return;
++ goto exit;
+ }
+ count++;
+ }
++
+ dev_err(dev, "Reset failed. (ret:%d)\n", ret);
++
++
++exit:
++ regcache_cache_only(max98388->regmap, true);
++ ret = regmap_update_bits(max98388->regmap,
++ MAX98388_R2000_SW_RESET,
++ MAX98388_SOFT_RESET, 0);
++ regcache_cache_only(max98388->regmap, false);
+ }
+
+ static int max98388_probe(struct snd_soc_component *component)
+@@ -420,6 +436,7 @@ static int max98388_probe(struct snd_soc_component *component)
+
+ /* Software Reset */
+ max98388_reset(max98388, component->dev);
++ usleep_range(400, 1000);
+
+ /* General channel source configuration */
+ regmap_write(max98388->regmap,
+@@ -812,6 +829,7 @@ static bool max98388_readable_register(struct device *dev,
+ case MAX98388_R210E_AUTO_RESTART:
+ case MAX98388_R210F_GLOBAL_EN:
+ case MAX98388_R22FF_REV_ID:
++ case MAX98388_R2000_SW_RESET:
+ return true;
+ default:
+ return false;
+@@ -824,6 +842,7 @@ static bool max98388_volatile_reg(struct device *dev, unsigned int reg)
+ case MAX98388_R2001_INT_RAW1 ... MAX98388_R2005_INT_STATE2:
+ case MAX98388_R210F_GLOBAL_EN:
+ case MAX98388_R22FF_REV_ID:
++ case MAX98388_R2000_SW_RESET:
+ return true;
+ default:
+ return false;
+@@ -868,6 +887,7 @@ static int max98388_resume(struct device *dev)
+
+ regcache_cache_only(max98388->regmap, false);
+ max98388_reset(max98388, dev);
++ usleep_range(400, 1000);
+ regcache_sync(max98388->regmap);
+
+ return 0;
+--
+GitLab
+
+From 39ecb253ecf7ed12e3019b72d7a226f0525dfaa3 Mon Sep 17 00:00:00 2001
+From: Venkata Prasad Potturu <venkataprasad.potturu@amd.com>
+Date: Fri, 20 Oct 2023 15:14:52 +0530
+Subject: [PATCH] ASoC: amd: Add new dmi entries and add condition check for
+ acp config flag
+
+Signed-off-by: Venkata Prasad Potturu <venkataprasad.potturu@amd.com>
+(cherry picked from commit 50f20ef9bcf6fdf6e6cc64a031be26fb9cb54849)
+---
+ sound/soc/amd/acp-config.c | 14 ++++++++++++++
+ sound/soc/amd/vangogh/pci-acp5x.c | 3 ++-
+ 2 files changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
+index f27c27580009..73d29a344e70 100644
+--- a/sound/soc/amd/acp-config.c
++++ b/sound/soc/amd/acp-config.c
+@@ -47,6 +47,20 @@ static const struct config_entry config_table[] = {
+ {}
+ },
+ },
++ {
++ .flags = FLAG_AMD_LEGACY,
++ .device = ACP_PCI_DEV_ID,
++ .dmi_table = (const struct dmi_system_id []) {
++ {
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
++ DMI_MATCH(DMI_PRODUCT_FAMILY, "Aerith"),
++ },
++ },
++ {}
++ },
++ },
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c
+index c4634a8a17cd..e29c4cc10e0f 100644
+--- a/sound/soc/amd/vangogh/pci-acp5x.c
++++ b/sound/soc/amd/vangogh/pci-acp5x.c
+@@ -13,6 +13,7 @@
+ #include <linux/pm_runtime.h>
+
+ #include "acp5x.h"
++#include "../mach-config.h"
+
+ struct acp5x_dev_data {
+ void __iomem *acp5x_base;
+@@ -131,7 +132,7 @@ static int snd_acp5x_probe(struct pci_dev *pci,
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+- if (flag)
++ if (flag != FLAG_AMD_LEGACY)
+ return -ENODEV;
+
+ irqflags = IRQF_SHARED;
+--
+GitLab
+
+From 4855323691ea7dd2288c51cd11f00561f9ba22df Mon Sep 17 00:00:00 2001
+From: Thomas Crider <gloriouseggroll@gmail.com>
+Date: Tue, 5 Dec 2023 05:13:41 -0500
+Subject: [PATCH] cs35l41 fixup
+
+---
+ sound/soc/amd/acp/acp-mach-common.c | 107 ++++++++++++++++++++++++++++
+ sound/soc/amd/acp/acp-mach.h | 4 ++
+ sound/soc/amd/acp/acp-sof-mach.c | 2 +
+ sound/soc/sof/ipc3-topology.c | 11 +--
+ sound/soc/sof/topology.c | 3 +-
+ 5 files changed, 122 insertions(+), 5 deletions(-)
+
+diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
+index a06af82b8..ae32748a5 100644
+--- a/sound/soc/amd/acp/acp-mach-common.c
++++ b/sound/soc/amd/acp/acp-mach-common.c
+@@ -26,6 +26,8 @@
+ #include "../../codecs/rt5682s.h"
+ #include "../../codecs/nau8825.h"
+ #include "../../codecs/nau8821.h"
++#include "../../codecs/cs35l41.h"
++
+ #include "acp-mach.h"
+
+ #define PCO_PLAT_CLK 48000000
+@@ -1243,6 +1245,78 @@ SND_SOC_DAILINK_DEF(nau8821,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00",
+ "nau8821-hifi")));
+
++static int acp_cs35l41_init(struct snd_soc_pcm_runtime *rtd)
++{
++ return 0;
++}
++
++static int acp_cs35l41_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++
++ runtime->hw.channels_max = DUAL_CHANNEL;
++ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
++ &constraints_channels);
++ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
++ &constraints_rates);
++ return 0;
++}
++
++static int acp_cs35l41_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++ struct snd_soc_card *card = rtd->card;
++ struct snd_soc_dai *codec_dai;
++ int ret, i;
++ unsigned int num_codecs = rtd->dai_link->num_codecs;
++ unsigned int bclk_val;
++
++ ret = 0;
++ for (i = 0; i < num_codecs; i++) {
++ codec_dai = asoc_rtd_to_codec(rtd, i);
++ if (strcmp(codec_dai->name, "cs35l41-pcm") == 0) {
++ switch (params_rate(params)) {
++ case 48000:
++ bclk_val = 1536000;
++ break;
++ default:
++ dev_err(card->dev, "Invalid Samplerate:0x%x\n",
++ params_rate(params));
++ return -EINVAL;
++ }
++ ret = snd_soc_component_set_sysclk(codec_dai->component,
++ 0, 0, bclk_val, SND_SOC_CLOCK_IN);
++ if (ret < 0) {
++ dev_err(card->dev, "failed to set sysclk for CS35l41 dai\n");
++ return ret;
++ }
++ }
++ }
++
++ return ret;
++}
++
++static struct snd_soc_codec_conf cs35l41_conf[] = {
++ {
++ .dlc = COMP_CODEC_CONF("spi-VLV1776:00"),
++ .name_prefix = "Left",
++ },
++ {
++ .dlc = COMP_CODEC_CONF("spi-VLV1776:01"),
++ .name_prefix = "Right",
++ },
++};
++
++static const struct snd_soc_ops acp_cs35l41_ops = {
++ .startup = acp_cs35l41_startup,
++ .hw_params = acp_cs35l41_hw_params,
++};
++
++SND_SOC_DAILINK_DEF(cs35l41,
++ DAILINK_COMP_ARRAY(COMP_CODEC("spi-VLV1776:00", "cs35l41-pcm"),
++ COMP_CODEC("spi-VLV1776:01", "cs35l41-pcm")));
++
+ /* Declare DMIC codec components */
+ SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
+@@ -1278,6 +1352,8 @@ SND_SOC_DAILINK_DEF(sof_hs,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs")));
+ SND_SOC_DAILINK_DEF(sof_hs_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs-virtual")));
++SND_SOC_DAILINK_DEF(sof_bt,
++ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-bt")));
+ SND_SOC_DAILINK_DEF(sof_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic")));
+ SND_SOC_DAILINK_DEF(pdm_dmic,
+@@ -1336,6 +1412,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
++ if (drv_data->bt_cpu_id)
++ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+@@ -1421,6 +1499,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
++ links[i].dpcm_capture = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+@@ -1453,6 +1532,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].dpcm_playback = 1;
++ links[i].dpcm_capture = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+@@ -1482,6 +1562,33 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
++ if (drv_data->amp_codec_id == CS35L41) {
++ links[i].codecs = cs35l41;
++ links[i].num_codecs = ARRAY_SIZE(cs35l41);
++ links[i].init = acp_cs35l41_init;
++ card->codec_conf = cs35l41_conf;
++ card->num_configs = ARRAY_SIZE(cs35l41_conf);
++ links[i].ops = &acp_cs35l41_ops;
++ }
++ i++;
++ }
++
++ if (drv_data->bt_cpu_id == I2S_BT) {
++ links[i].name = "acp-bt-codec";
++ links[i].id = BT_BE_ID;
++ links[i].cpus = sof_bt;
++ links[i].num_cpus = ARRAY_SIZE(sof_bt);
++ links[i].platforms = sof_component;
++ links[i].num_platforms = ARRAY_SIZE(sof_component);
++ links[i].dpcm_playback = 1;
++ links[i].dpcm_capture = 1;
++ links[i].nonatomic = true;
++ links[i].no_pcm = 1;
++ if (!drv_data->bt_codec_id) {
++ /* Use dummy codec if codec id not specified */
++ links[i].codecs = &asoc_dummy_dlc;
++ links[i].num_codecs = 1;
++ }
+ i++;
+ }
+
+diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
+index 2b3ec6594..6feef5a93 100644
+--- a/sound/soc/amd/acp/acp-mach.h
++++ b/sound/soc/amd/acp/acp-mach.h
+@@ -23,6 +23,7 @@
+ enum be_id {
+ HEADSET_BE_ID = 0,
+ AMP_BE_ID,
++ BT_BE_ID,
+ DMIC_BE_ID,
+ };
+
+@@ -41,6 +42,7 @@ enum codec_endpoints {
+ MAX98360A,
+ RT5682S,
+ NAU8825,
++ CS35L41,
+ NAU8821,
+ MAX98388,
+ };
+@@ -53,9 +55,11 @@ enum platform_end_point {
+ struct acp_card_drvdata {
+ unsigned int hs_cpu_id;
+ unsigned int amp_cpu_id;
++ unsigned int bt_cpu_id;
+ unsigned int dmic_cpu_id;
+ unsigned int hs_codec_id;
+ unsigned int amp_codec_id;
++ unsigned int bt_codec_id;
+ unsigned int dmic_codec_id;
+ unsigned int dai_fmt;
+ unsigned int platform;
+diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
+index 5223033a1..45d4ce17c 100644
+--- a/sound/soc/amd/acp/acp-sof-mach.c
++++ b/sound/soc/amd/acp/acp-sof-mach.c
+@@ -86,9 +86,11 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
+ static struct acp_card_drvdata sof_nau8821_max98388_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_HS,
++ .bt_cpu_id = I2S_BT,
+ .dmic_cpu_id = NONE,
+ .hs_codec_id = NAU8821,
+ .amp_codec_id = MAX98388,
++ .bt_codec_id = NONE,
+ .dmic_codec_id = NONE,
+ .soc_mclk = true,
+ .tdm_mode = false,
+diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
+index ba4ef290b..2f070f907 100644
+--- a/sound/soc/sof/ipc3-topology.c
++++ b/sound/soc/sof/ipc3-topology.c
+@@ -1174,6 +1174,7 @@ static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_
+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
++ int ret;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+@@ -1182,12 +1183,14 @@ static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_
+ memset(&config->acpbt, 0, sizeof(config->acpbt));
+ config->hdr.size = size;
+
+- config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+- config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
++ ret = sof_update_ipc_object(scomp, &config->acpbt, SOF_ACPI2S_TOKENS, slink->tuples,
++ slink->num_tuples, size, slink->num_hw_configs);
++ if (ret < 0)
++ return ret;
+
+- dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n",
++ dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d tdm_mode %d\n",
+ config->dai_index, config->acpbt.tdm_slots,
+- config->acpbt.fsync_rate);
++ config->acpbt.fsync_rate, config->acpbt.tdm_mode);
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
+index a3a3af252..a634ae05f 100644
+--- a/sound/soc/sof/topology.c
++++ b/sound/soc/sof/topology.c
+@@ -289,7 +289,7 @@ static const struct sof_dai_types sof_dais[] = {
+ {"ALH", SOF_DAI_INTEL_ALH},
+ {"SAI", SOF_DAI_IMX_SAI},
+ {"ESAI", SOF_DAI_IMX_ESAI},
+- {"ACP", SOF_DAI_AMD_BT},
++ {"ACPBT", SOF_DAI_AMD_BT},
+ {"ACPSP", SOF_DAI_AMD_SP},
+ {"ACPDMIC", SOF_DAI_AMD_DMIC},
+ {"ACPHS", SOF_DAI_AMD_HS},
+@@ -1953,6 +1953,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
+ token_id = SOF_ACPDMIC_TOKENS;
+ num_tuples += token_list[SOF_ACPDMIC_TOKENS].count;
+ break;
++ case SOF_DAI_AMD_BT:
+ case SOF_DAI_AMD_SP:
+ case SOF_DAI_AMD_HS:
+ case SOF_DAI_AMD_SP_VIRTUAL:
+--
+2.43.0
+
diff --git a/SOURCES/steamdeck-oled-bt.patch b/SOURCES/steamdeck-oled-bt.patch
new file mode 100644
index 0000000..20cf681
--- /dev/null
+++ b/SOURCES/steamdeck-oled-bt.patch
@@ -0,0 +1,239 @@
+From fca3761de38864b0422006aaaf9ce8e0aba5e316 Mon Sep 17 00:00:00 2001
+From: Thomas Crider <gloriouseggroll@gmail.com>
+Date: Sat, 2 Dec 2023 05:07:16 -0500
+Subject: [PATCH] steamdeck-bt-unified
+
+---
+ drivers/bluetooth/btqca.c | 78 +++++++++++++++++++++++++++++++++++++
+ drivers/bluetooth/btqca.h | 3 ++
+ drivers/bluetooth/hci_qca.c | 9 ++++-
+ net/bluetooth/hci_sync.c | 10 +++--
+ 4 files changed, 95 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
+index 5a35ac413..de2195b72 100644
+--- a/drivers/bluetooth/btqca.c
++++ b/drivers/bluetooth/btqca.c
+@@ -205,6 +205,48 @@ static int qca_send_reset(struct hci_dev *hdev)
+ return 0;
+ }
+
++static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
++{
++ u8 cmd;
++ struct sk_buff *skb;
++ struct edl_event_hdr *edl;
++ int err = 0;
++ int bid_len;
++
++ bt_dev_dbg(hdev, "QCA read board ID");
++
++ cmd = EDL_GET_BID_REQ_CMD;
++ skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
++ &cmd, 0, HCI_INIT_TIMEOUT);
++ if (IS_ERR(skb)) {
++ err = PTR_ERR(skb);
++ bt_dev_err(hdev, "Reading QCA board ID failed (%d)", err);
++ return err;
++ }
++
++ edl = skb_pull_data(skb, sizeof(*edl));
++ if (!edl) {
++ bt_dev_err(hdev, "QCA read board ID with no header");
++ err = -EILSEQ;
++ goto out;
++ }
++
++ if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
++ edl->rtype != EDL_GET_BID_REQ_CMD) {
++ bt_dev_err(hdev, "QCA Wrong packet: %d %d", edl->cresp, edl->rtype);
++ err = -EIO;
++ goto out;
++ }
++
++ bid_len = edl->data[0];
++ *bid = (edl->data[1] << 8) + edl->data[2];
++ bt_dev_info(hdev, "%s: bid len = %x, bid = %x", __func__, bid_len, *bid);
++
++out:
++ kfree_skb(skb);
++ return err;
++}
++
+ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
+ {
+ struct sk_buff *skb;
+@@ -574,6 +616,30 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+ }
+ EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
+
++static void qca_generate_nvm_name(struct hci_dev *hdev, char *fwname,
++ size_t max_size, struct qca_btsoc_version ver, u16 bid)
++{
++ u8 rom_ver = 0;
++ u32 soc_ver;
++ const char *variant;
++
++ soc_ver = get_soc_ver(ver.soc_id, ver.rom_ver);
++ rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
++
++ if ((ver.soc_id & 0x0000ff00) == QCA_HSP_GF_SOC_ID) /* hsp gf chip */
++ variant = "g";
++ else
++ variant = "";
++
++ if (bid == 0x0)
++ snprintf(fwname, max_size, "qca/hpnv%02x%s.bin", rom_ver, variant);
++ else
++ snprintf(fwname, max_size, "qca/hpnv%02x%s.%x",
++ rom_ver, variant, bid);
++
++ bt_dev_info(hdev, "%s: nvm name is %s", __func__, fwname);
++}
++
+ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
+ enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
+ const char *firmware_name)
+@@ -582,6 +648,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
+ int err;
+ u8 rom_ver = 0;
+ u32 soc_ver;
++ u16 boardid = 0;
+
+ bt_dev_dbg(hdev, "QCA setup on UART");
+
+@@ -605,6 +672,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
+ /* Download rampatch file */
+ config.type = TLV_TYPE_PATCH;
+ switch (soc_type) {
++ case QCA_QCA2066:
++ snprintf(config.fwname, sizeof(config.fwname),
++ "qca/hpbtfw%02x.tlv", rom_ver);
++ break;
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+@@ -649,6 +720,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
+ /* Give the controller some time to get ready to receive the NVM */
+ msleep(10);
+
++ if (soc_type == QCA_QCA2066)
++ qca_read_fw_board_id(hdev, &boardid);
++
+ /* Download NVM configuration */
+ config.type = TLV_TYPE_NVM;
+ if (firmware_name) {
+@@ -656,6 +730,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
+ "qca/%s", firmware_name);
+ } else {
+ switch (soc_type) {
++ case QCA_QCA2066:
++ qca_generate_nvm_name(hdev, config.fwname, sizeof(config.fwname),
++ ver, boardid);
++ break;
+ case QCA_WCN3990:
+ case QCA_WCN3991:
+ case QCA_WCN3998:
+diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
+index 03bff5c00..ffed9ea8a 100644
+--- a/drivers/bluetooth/btqca.h
++++ b/drivers/bluetooth/btqca.h
+@@ -13,6 +13,7 @@
+ #define EDL_PATCH_TLV_REQ_CMD (0x1E)
+ #define EDL_GET_BUILD_INFO_CMD (0x20)
+ #define EDL_NVM_ACCESS_SET_REQ_CMD (0x01)
++#define EDL_GET_BID_REQ_CMD (0x23)
+ #define EDL_PATCH_CONFIG_CMD (0x28)
+ #define MAX_SIZE_PER_TLV_SEGMENT (243)
+ #define QCA_PRE_SHUTDOWN_CMD (0xFC08)
+@@ -48,6 +49,7 @@
+
+ #define QCA_FW_BUILD_VER_LEN 255
+
++#define QCA_HSP_GF_SOC_ID 0x1200
+
+ enum qca_baudrate {
+ QCA_BAUDRATE_115200 = 0,
+@@ -146,6 +148,7 @@ enum qca_btsoc_type {
+ QCA_WCN3990,
+ QCA_WCN3998,
+ QCA_WCN3991,
++ QCA_QCA2066,
+ QCA_QCA6390,
+ QCA_WCN6750,
+ QCA_WCN6855,
+diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
+index 4b57e15f9..891c25ffc 100644
+--- a/drivers/bluetooth/hci_qca.c
++++ b/drivers/bluetooth/hci_qca.c
+@@ -1861,7 +1861,7 @@ static int qca_setup(struct hci_uart *hu)
+ break;
+
+ default:
+- soc_name = "ROME/QCA6390";
++ soc_name = "ROME/QCA6390/QCA2066";
+ }
+ bt_dev_info(hdev, "setting up %s", soc_name);
+
+@@ -1987,6 +1987,11 @@ static const struct hci_uart_proto qca_proto = {
+ .dequeue = qca_dequeue,
+ };
+
++static const struct qca_device_data qca_soc_data_qca2066 = {
++ .soc_type = QCA_QCA2066,
++ .num_vregs = 0,
++};
++
+ static const struct qca_device_data qca_soc_data_wcn3988 __maybe_unused = {
+ .soc_type = QCA_WCN3988,
+ .vregs = (struct qca_vreg []) {
+@@ -2569,6 +2574,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = {
+ { .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750},
+ { .compatible = "qcom,wcn6855-bt", .data = &qca_soc_data_wcn6855},
+ { .compatible = "qcom,wcn7850-bt", .data = &qca_soc_data_wcn7850},
++ { .compatible = "qcom,qca2066-bt", .data = &qca_soc_data_qca2066},
+ { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
+@@ -2580,6 +2586,7 @@ static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
+ { "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+ { "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+ { "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 },
++ { "QCOM2066", (kernel_ulong_t)&qca_soc_data_qca2066 },
+ { },
+ };
+ MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
+diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
+index 9e71362c0..ac5c0cafd 100644
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -3800,12 +3800,14 @@ static int hci_set_event_mask_sync(struct hci_dev *hdev)
+ if (lmp_bredr_capable(hdev)) {
+ events[4] |= 0x01; /* Flow Specification Complete */
+
+- /* Don't set Disconnect Complete when suspended as that
+- * would wakeup the host when disconnecting due to
++ /* Don't set Disconnect Complete and mode change when suspended
++ * as that would wakeup the host when disconnecting due to
+ * suspend.
+ */
+- if (hdev->suspended)
++ if (hdev->suspended){
+ events[0] &= 0xef;
++ events[2] &= 0xf7;
++ }
+ } else {
+ /* Use a different default for LE-only devices */
+ memset(events, 0, sizeof(events));
+@@ -5931,7 +5933,7 @@ int hci_suspend_sync(struct hci_dev *hdev)
+
+ if (hci_conn_count(hdev)) {
+ /* Soft disconnect everything (power off) */
+- err = hci_disconnect_all_sync(hdev, HCI_ERROR_REMOTE_POWER_OFF);
++ err = hci_disconnect_all_sync(hdev, HCI_ERROR_REMOTE_USER_TERM);
+ if (err) {
+ /* Set state to BT_RUNNING so resume doesn't notify */
+ hdev->suspend_state = BT_RUNNING;
+--
+2.43.0
+
diff --git a/SOURCES/steamdeck-oled-hw-quirks.patch b/SOURCES/steamdeck-oled-hw-quirks.patch
new file mode 100644
index 0000000..a2b1228
--- /dev/null
+++ b/SOURCES/steamdeck-oled-hw-quirks.patch
@@ -0,0 +1,395 @@
+From e08dfd29027d6c57ef41f3d8c47d389a144e501b Mon Sep 17 00:00:00 2001
+From: John Schoenick <johns@valvesoftware.com>
+Date: Tue, 11 Jul 2023 15:27:33 -0700
+Subject: [PATCH] drm: panel-orientation-quirks: Add quirk for Valve Galileo
+
+---
+ drivers/gpu/drm/drm_panel_orientation_quirks.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c
+index 0cb646cb04ee1..014101e92b5a7 100644
+--- a/drivers/gpu/drm/drm_panel_orientation_quirks.c
++++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c
+@@ -395,6 +395,13 @@ static const struct dmi_system_id orientation_data[] = {
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"),
+ },
+ .driver_data = (void *)&lcd800x1280_rightside_up,
++ }, { /* Valve Steam Deck */
++ .matches = {
++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"),
++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"),
++ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"),
++ },
++ .driver_data = (void *)&lcd800x1280_rightside_up,
+ }, { /* VIOS LTH17 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
+--
+GitLab
+
+From 1fc12492bc3045d7e55d85f6dd559b6766a7e564 Mon Sep 17 00:00:00 2001
+From: Swapnil Patel <swapatel@amd.com>
+Date: Thu, 2 Nov 2023 16:16:49 -0400
+Subject: [PATCH] Disable modes with >1200 MHz Pixel clocks when connected via
+ dock
+
+(cherry picked from commit 36301114e8a32e7f13985cbbeff7282d4c599aed)
+---
+ drivers/gpu/drm/amd/display/dc/link/link_validation.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.c b/drivers/gpu/drm/amd/display/dc/link/link_validation.c
+index e8b2fc4002a52..3c69d860e1c9d 100644
+--- a/drivers/gpu/drm/amd/display/dc/link/link_validation.c
++++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.c
+@@ -35,6 +35,8 @@
+
+ #define DC_LOGGER_INIT(logger)
+
++static const uint8_t DP_SINK_BRANCH_DEV_NAME_KT50X0[] = "KT50X0!";
++
+ static uint32_t get_tmds_output_pixel_clock_100hz(const struct dc_crtc_timing *timing)
+ {
+
+@@ -277,6 +279,15 @@ static bool dp_validate_mode_timing(
+ timing->v_addressable == (uint32_t) 480)
+ return true;
+
++ if (link->ctx->dce_version == DCN_VERSION_3_01 &&
++ link->dpcd_caps.sink_dev_id == DP_BRANCH_DEVICE_ID_0060AD &&
++ memcmp(&link->dpcd_caps.branch_dev_name,
++ DP_SINK_BRANCH_DEV_NAME_KT50X0,
++ sizeof(link->dpcd_caps.branch_dev_name)) == 0) {
++ if (timing->pix_clk_100hz / 10 >= (uint32_t) 1200000)
++ return false; /* KT50X0 does not support Pxl clock >= 1200MHz */
++ }
++
+ link_setting = dp_get_verified_link_cap(link);
+
+ /* TODO: DYNAMIC_VALIDATION needs to be implemented */
+--
+GitLab
+
+From a32cc4f110bcd8d4595ff0812a72a521e99006ac Mon Sep 17 00:00:00 2001
+From: Keith Mikoleit <keithm@valvesoftware.com>
+Date: Fri, 22 Sep 2023 17:30:44 -0700
+Subject: [PATCH] drm/amd/display: change default edp brightness check to min 1
+ nit
+
+---
+ .../amd/display/dc/link/protocols/link_edp_panel_control.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
+index 2039a345f23a1..e4626f2072f0f 100644
+--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
++++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
+@@ -266,8 +266,8 @@ bool set_default_brightness_aux(struct dc_link *link)
+ if (link && link->dpcd_sink_ext_caps.bits.oled == 1) {
+ if (!read_default_bl_aux(link, &default_backlight))
+ default_backlight = 150000;
+- // if < 5 nits or > 5000, it might be wrong readback
+- if (default_backlight < 5000 || default_backlight > 5000000)
++ // if < 1 nits or > 5000, it might be wrong readback
++ if (default_backlight < 1000 || default_backlight > 5000000)
+ default_backlight = 150000; //
+
+ return edp_set_backlight_level_nits(link, true,
+--
+GitLab
+From b59fed802470f07fafe72f6a2bdda2163da5ba33 Mon Sep 17 00:00:00 2001
+From: Swapnil Patel <Swapnil.Patel@amd.com>
+Date: Tue, 26 Sep 2023 16:24:25 -0400
+Subject: [PATCH] drm/amd/display: Don't add common modes for eDP connector
+
+[Why]
+Currently, we are adding various common modes to drm_connector for DP
+and eDP connection even if they aren't part of EDID. This results in
+unsupported modes getting added to eDP connector.
+
+[How]
+Add common modes to drm_connector only for DP connection.
+
+Signed-off-by: Swapnil Patel <swapnil.patel@amd.com>
+---
+ drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+index 85159cd0bfcd..2efefca8143b 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -7332,7 +7332,8 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
+ drm_add_modes_noedid(connector, 1920, 1080);
+ } else {
+ amdgpu_dm_connector_ddc_get_modes(connector, edid);
+- amdgpu_dm_connector_add_common_modes(encoder, connector);
++ if (connector->connector_type != DRM_MODE_CONNECTOR_eDP)
++ amdgpu_dm_connector_add_common_modes(encoder, connector);
+ amdgpu_dm_connector_add_freesync_modes(connector, edid);
+ }
+ amdgpu_dm_fbc_init(connector);
+--
+GitLab
+
+From 8f24ff69523cf3059e23ba6aa41d09b0c00f2986 Mon Sep 17 00:00:00 2001
+From: Jeremy Selan <jeremys@valvesoftware.com>
+Date: Wed, 28 Apr 2021 14:33:36 -0700
+Subject: [PATCH] drm/amd: bump backlight brightness precision from 8 ->
+ 16-bits
+
+Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
+[Fwd-ported to DC_VER 3.2.237]
+Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h | 2 --
+ drivers/gpu/drm/amd/amdgpu/atombios_encoders.c | 10 +++++++---
+ drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 +++
+ 3 files changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+index b8633df418d43..77a1bedaee98c 100644
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+@@ -416,8 +416,6 @@
+ struct drm_property *regamma_tf_property;
+ };
+
+-#define AMDGPU_MAX_BL_LEVEL 0xFF
+-
+ struct amdgpu_backlight_privdata {
+ struct amdgpu_encoder *encoder;
+ uint8_t negative;
+diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c
+index 18ae9433e463d..37cb6e7ba47a2 100644
+--- a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c
++++ b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c
+@@ -39,6 +39,10 @@
+ #include <linux/backlight.h>
+ #include "bif/bif_4_1_d.h"
+
++
++/* Maximum backlight level. */
++#define AMDGPU_ATOM_MAX_BL_LEVEL 0xFF
++
+ u8
+ amdgpu_atombios_encoder_get_backlight_level_from_reg(struct amdgpu_device *adev)
+ {
+@@ -127,8 +131,8 @@ static u8 amdgpu_atombios_encoder_backlight_level(struct backlight_device *bd)
+ /* Convert brightness to hardware level */
+ if (bd->props.brightness < 0)
+ level = 0;
+- else if (bd->props.brightness > AMDGPU_MAX_BL_LEVEL)
+- level = AMDGPU_MAX_BL_LEVEL;
++ else if (bd->props.brightness > AMDGPU_ATOM_MAX_BL_LEVEL)
++ level = AMDGPU_ATOM_MAX_BL_LEVEL;
+ else
+ level = bd->props.brightness;
+
+@@ -198,7 +202,7 @@ void amdgpu_atombios_encoder_init_backlight(struct amdgpu_encoder *amdgpu_encode
+ }
+
+ memset(&props, 0, sizeof(props));
+- props.max_brightness = AMDGPU_MAX_BL_LEVEL;
++ props.max_brightness = AMDGPU_ATOM_MAX_BL_LEVEL;
+ props.type = BACKLIGHT_RAW;
+ snprintf(bl_name, sizeof(bl_name),
+ "amdgpu_bl%d", dev->primary->index);
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+index 22e9d8e91ed49..e1a77a0d66336 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -146,6 +146,9 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
+ /* Number of bytes in PSP footer for firmware. */
+ #define PSP_FOOTER_BYTES 0x100
+
++/* Maximum backlight level. */
++#define AMDGPU_MAX_BL_LEVEL 0xFFFF
++
+ /**
+ * DOC: overview
+ *
+--
+GitLab
+
+From 1fdb02c119c85b18b48150c0f3ce97e1940c63f1 Mon Sep 17 00:00:00 2001
+From: Jeremy Selan <jeremys@valvesoftware.com>
+Date: Fri, 12 Nov 2021 10:03:20 -0800
+Subject: [PATCH] amd/drm: override backlight min value from 12 -> 0
+
+This overrides backlight handling to the FULL range of the device {0,max}
+with no additional interpretation / rescaling. This places the burden
+of selecting appropriate device-specific minimum ranges fully on
+userspace.
+
+Device defaults provided by ACPI/ATIF are logged, but ignored.
+
+[Merge, add line breaks]
+Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
+---
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 25 ++++++++++++++++---
+ 1 file changed, 22 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+index e1a77a0d66336..8e61c86819fe2 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -4032,7 +4032,7 @@
+ return 0;
+ }
+
+-#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
++#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 0
+ #define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
+ #define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
+
+@@ -4050,11 +4050,27 @@
+ amdgpu_acpi_get_backlight_caps(&caps);
+ if (caps.caps_valid) {
+ dm->backlight_caps[bl_idx].caps_valid = true;
++
++ printk(KERN_NOTICE"VLV Successfully queried backlight range over ACPI: %d %d\n",
++ (int) caps.min_input_signal, (int) caps.max_input_signal);
++
++ if ( caps.min_input_signal != AMDGPU_DM_DEFAULT_MIN_BACKLIGHT ||
++ caps.max_input_signal != AMDGPU_DM_DEFAULT_MAX_BACKLIGHT )
++ {
++ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
++ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
++
++ printk(KERN_NOTICE"VLV OVERRIDE backlight range: %d %d\n",
++ (int) caps.min_input_signal, (int) caps.max_input_signal);
++ }
++
+ if (caps.aux_support)
+ return;
+ dm->backlight_caps[bl_idx].min_input_signal = caps.min_input_signal;
+ dm->backlight_caps[bl_idx].max_input_signal = caps.max_input_signal;
+ } else {
++ printk(KERN_NOTICE"VLV ACPI does not provide backlight range, using defaults: %d %d\n",
++ AMDGPU_DM_DEFAULT_MIN_BACKLIGHT, AMDGPU_DM_DEFAULT_MAX_BACKLIGHT);
+ dm->backlight_caps[bl_idx].min_input_signal =
+ AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ dm->backlight_caps[bl_idx].max_input_signal =
+@@ -4064,6 +4080,9 @@
+ if (dm->backlight_caps[bl_idx].aux_support)
+ return;
+
++ printk(KERN_NOTICE"VLV Kernel built without ACPI. using backlight range defaults: %d %d\n",
++ AMDGPU_DM_DEFAULT_MIN_BACKLIGHT, AMDGPU_DM_DEFAULT_MAX_BACKLIGHT);
++
+ dm->backlight_caps[bl_idx].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ dm->backlight_caps[bl_idx].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ #endif
+@@ -4095,7 +4114,7 @@
+ if (!get_brightness_range(caps, &min, &max))
+ return brightness;
+
+- // Rescale 0..255 to min..max
++ // Rescale 0..AMDGPU_MAX_BL_LEVEL to min..max
+ return min + DIV_ROUND_CLOSEST((max - min) * brightness,
+ AMDGPU_MAX_BL_LEVEL);
+ }
+@@ -4110,7 +4129,7 @@
+
+ if (brightness < min)
+ return 0;
+- // Rescale min..max to 0..255
++ // Rescale min..max to 0..AMDGPU_MAX_BL_LEVEL
+ return DIV_ROUND_CLOSEST(AMDGPU_MAX_BL_LEVEL * (brightness - min),
+ max - min);
+ }
+--
+GitLab
+
+From ab7d646eacf9f1c745d284e293211569a4428573 Mon Sep 17 00:00:00 2001
+From: "Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>
+Date: Wed, 8 Nov 2023 19:45:52 -0800
+Subject: [PATCH] amdgpu: fix Galileo desktop brightness overflowing
+
+500k uNits * 65k max brightness range overflows in the conversion code.
+Scale back brightness range to 12bit max.
+---
+ drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+index 6d7df6ae890a..c1aeeb927a0b 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -147,7 +147,7 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
+ #define PSP_FOOTER_BYTES 0x100
+
+ /* Maximum backlight level. */
+-#define AMDGPU_MAX_BL_LEVEL 0xFFFF
++#define AMDGPU_MAX_BL_LEVEL 0xFFF
+
+ /**
+ * DOC: overview
+--
+GitLab
+
+From a92d28a30aac79b0656d0bf5833e6fe0625e31af Mon Sep 17 00:00:00 2001
+From: Andres Rodriguez <andresr@valvesoftware.com>
+Date: Wed, 22 Nov 2023 11:28:35 -0800
+Subject: [PATCH] Revert "PCI: Prevent xHCI driver from claiming AMD VanGogh
+ USB3 DRD device"
+
+This reverts commit a4904c47fcd7fc9152b6b04409feac1130e2033d.
+
+This results in some USB devices to fail to enumerate. Revert pending
+further investigation.
+---
+ drivers/pci/quirks.c | 8 +++-----
+ include/linux/pci_ids.h | 1 -
+ 2 files changed, 3 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 30e7c627f21a7..472fa2c8ebcec 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -595,7 +595,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_
+ /*
+ * In the AMD NL platform, this device ([1022:7912]) has a class code of
+ * PCI_CLASS_SERIAL_USB_XHCI (0x0c0330), which means the xhci driver will
+- * claim it. The same applies on the VanGogh platform device ([1022:163a]).
++ * claim it.
+ *
+ * But the dwc3 driver is a more specific driver for this device, and we'd
+ * prefer to use it instead of xhci. To prevent xhci from claiming the
+@@ -603,7 +603,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_
+ * defines as "USB device (not host controller)". The dwc3 driver can then
+ * claim it based on its Vendor and Device ID.
+ */
+-static void quirk_amd_dwc_class(struct pci_dev *pdev)
++static void quirk_amd_nl_class(struct pci_dev *pdev)
+ {
+ u32 class = pdev->class;
+
+@@ -613,9 +613,7 @@ static void quirk_amd_dwc_class(struct pci_dev *pdev)
+ class, pdev->class);
+ }
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB,
+- quirk_amd_dwc_class);
+-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VANGOGH_USB,
+- quirk_amd_dwc_class);
++ quirk_amd_nl_class);
+
+ /*
+ * Synopsys USB 3.x host HAPS platform has a class code of
+diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
+index b76ff08506181..95f33dadb2be2 100644
+--- a/include/linux/pci_ids.h
++++ b/include/linux/pci_ids.h
+@@ -568,7 +568,6 @@
+ #define PCI_DEVICE_ID_AMD_1AH_M00H_DF_F3 0x12c3
+ #define PCI_DEVICE_ID_AMD_1AH_M20H_DF_F3 0x16fb
+ #define PCI_DEVICE_ID_AMD_MI200_DF_F3 0x14d3
+-#define PCI_DEVICE_ID_AMD_VANGOGH_USB 0x163a
+ #define PCI_DEVICE_ID_AMD_CNB17H_F3 0x1703
+ #define PCI_DEVICE_ID_AMD_LANCE 0x2000
+ #define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001
+--
+GitLab
+
diff --git a/SOURCES/steamdeck-oled-wifi.patch b/SOURCES/steamdeck-oled-wifi.patch
new file mode 100644
index 0000000..39ae50a
--- /dev/null
+++ b/SOURCES/steamdeck-oled-wifi.patch
@@ -0,0 +1,3753 @@
+From 127870bd4c46edfc7bbc58476d7e02cf9a25ea05 Mon Sep 17 00:00:00 2001
+From: Thomas Crider <gloriouseggroll@gmail.com>
+Date: Sat, 2 Dec 2023 03:04:44 -0500
+Subject: [PATCH] steamdeck-wifi-unified
+
+---
+ drivers/net/wireless/ath/ath11k/Makefile | 4 +-
+ drivers/net/wireless/ath/ath11k/ce.c | 6 +-
+ drivers/net/wireless/ath/ath11k/core.c | 159 +++++
+ drivers/net/wireless/ath/ath11k/core.h | 60 ++
+ drivers/net/wireless/ath/ath11k/debugfs.c | 79 +++
+ drivers/net/wireless/ath/ath11k/dp.c | 12 +-
+ drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +-
+ drivers/net/wireless/ath/ath11k/hal.c | 9 +-
+ drivers/net/wireless/ath/ath11k/hif.h | 8 +
+ drivers/net/wireless/ath/ath11k/hw.c | 13 +
+ drivers/net/wireless/ath/ath11k/hw.h | 8 +
+ drivers/net/wireless/ath/ath11k/mac.c | 709 +++++++++++++++++++--
+ drivers/net/wireless/ath/ath11k/mac.h | 7 +-
+ drivers/net/wireless/ath/ath11k/mhi.c | 18 +-
+ drivers/net/wireless/ath/ath11k/mhi.h | 1 +
+ drivers/net/wireless/ath/ath11k/pci.c | 181 +++++-
+ drivers/net/wireless/ath/ath11k/pci.h | 63 ++
+ drivers/net/wireless/ath/ath11k/pcic.c | 22 +-
+ drivers/net/wireless/ath/ath11k/qmi.c | 65 +-
+ drivers/net/wireless/ath/ath11k/qmi.h | 1 +
+ drivers/net/wireless/ath/ath11k/reg.c | 189 ++++--
+ drivers/net/wireless/ath/ath11k/reg.h | 7 +-
+ drivers/net/wireless/ath/ath11k/testmode.h | 18 +-
+ drivers/net/wireless/ath/ath11k/unitest.c | 96 +++
+ drivers/net/wireless/ath/ath11k/unitest.h | 44 ++
+ drivers/net/wireless/ath/ath11k/wmi.c | 307 +++++++--
+ drivers/net/wireless/ath/ath11k/wmi.h | 182 ++++++
+ include/net/cfg80211.h | 5 +
+ include/net/regulatory.h | 1 +
+ include/uapi/linux/nl80211.h | 11 +-
+ kernel/dma/direct.c | 1 +
+ net/wireless/nl80211.c | 18 +
+ net/wireless/reg.c | 17 +
+ 33 files changed, 2131 insertions(+), 192 deletions(-)
+ create mode 100644 drivers/net/wireless/ath/ath11k/unitest.c
+ create mode 100644 drivers/net/wireless/ath/ath11k/unitest.h
+
+diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile
+index cc47e0114..f591a12a8 100644
+--- a/drivers/net/wireless/ath/ath11k/Makefile
++++ b/drivers/net/wireless/ath/ath11k/Makefile
+@@ -17,7 +17,9 @@ ath11k-y += core.o \
+ peer.o \
+ dbring.o \
+ hw.o \
+- pcic.o
++ pcic.o \
++ wow.o \
++ unitest.o
+
+ ath11k-$(CONFIG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
+ ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o
+diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c
+index 289d47ae9..4d04c49ee 100644
+--- a/drivers/net/wireless/ath/ath11k/ce.c
++++ b/drivers/net/wireless/ath/ath11k/ce.c
+@@ -627,9 +627,9 @@ ath11k_ce_alloc_ring(struct ath11k_base *ab, int nentries, int desc_sz)
+ * coherent DMA are unsupported
+ */
+ ce_ring->base_addr_owner_space_unaligned =
+- dma_alloc_coherent(ab->dev,
+- nentries * desc_sz + CE_DESC_RING_ALIGN,
+- &base_addr, GFP_KERNEL);
++ ath11k_core_dma_alloc_coherent(ab->dev,
++ nentries * desc_sz + CE_DESC_RING_ALIGN,
++ &base_addr, GFP_KERNEL | GFP_DMA32);
+ if (!ce_ring->base_addr_owner_space_unaligned) {
+ kfree(ce_ring);
+ return ERR_PTR(-ENOMEM);
+diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
+index fc7c4564a..2cbfd20bd 100644
+--- a/drivers/net/wireless/ath/ath11k/core.c
++++ b/drivers/net/wireless/ath/ath11k/core.c
+@@ -9,6 +9,7 @@
+ #include <linux/remoteproc.h>
+ #include <linux/firmware.h>
+ #include <linux/of.h>
++#include <linux/dma-direct.h>
+
+ #include "core.h"
+ #include "dp_tx.h"
+@@ -16,6 +17,7 @@
+ #include "debug.h"
+ #include "hif.h"
+ #include "wow.h"
++#include "wmi.h"
+
+ unsigned int ath11k_debug_mask;
+ EXPORT_SYMBOL(ath11k_debug_mask);
+@@ -121,6 +123,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .tcl_ring_retry = true,
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE,
+ .smp2p_wow_exit = false,
++ .coex_isolation = true,
+ },
+ {
+ .hw_rev = ATH11K_HW_IPQ6018_HW10,
+@@ -204,6 +207,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE,
+ .smp2p_wow_exit = false,
+ .support_fw_mac_sequence = false,
++ .coex_isolation = false,
++ .support_fw_mac_sequence = false,
+ },
+ {
+ .name = "qca6390 hw2.0",
+@@ -288,6 +293,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .tcl_ring_retry = true,
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE,
+ .smp2p_wow_exit = false,
++ .coex_isolation = false,
+ .support_fw_mac_sequence = true,
+ },
+ {
+@@ -371,6 +377,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE,
+ .smp2p_wow_exit = false,
+ .support_fw_mac_sequence = false,
++ .coex_isolation = false,
++ .support_fw_mac_sequence = false,
+ },
+ {
+ .name = "wcn6855 hw2.0",
+@@ -410,6 +418,78 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .fragment_160mhz = false,
+ },
+
++ .interface_modes = BIT(NL80211_IFTYPE_STATION) |
++ BIT(NL80211_IFTYPE_AP),
++ .supports_monitor = false,
++ .full_monitor_mode = false,
++ .supports_shadow_regs = true,
++ .idle_ps = true,
++ .supports_sta_ps = true,
++ .coldboot_cal_mm = false,
++ .coldboot_cal_ftm = false,
++ .fw_mem_mode = 0,
++ .num_vdevs = 16 + 1,
++ .num_peers = 512,
++ .supports_suspend = true,
++ .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855),
++ .supports_regdb = true,
++ .fix_l1ss = false,
++ .credit_flow = true,
++ .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390,
++ .hal_params = &ath11k_hw_hal_params_qca6390,
++ .supports_dynamic_smps_6ghz = false,
++ .alloc_cacheable_memory = false,
++ .supports_rssi_stats = true,
++ .fw_wmi_diag_event = true,
++ .current_cc_support = true,
++ .dbr_debug_support = false,
++ .coex_isolation = false,
++ .global_reset = true,
++ .bios_sar_capa = &ath11k_hw_sar_capa_wcn6855,
++ .m3_fw_support = true,
++ .fixed_bdf_addr = false,
++ .fixed_mem_region = false,
++ .static_window_map = false,
++ .hybrid_bus_type = false,
++ .fixed_fw_mem = false,
++ .support_off_channel_tx = true,
++ },
++ {
++ .name = "qca206x hw2.1",
++ .hw_rev = ATH11K_HW_QCA206X_HW21,
++ .fw = {
++ .dir = "QCA206X/hw2.1",
++ .board_size = 256 * 1024,
++ .cal_offset = 128 * 1024,
++ },
++ .max_radios = 3,
++ .bdf_addr = 0x4B0C0000,
++ .hw_ops = &wcn6855_ops,
++ .ring_mask = &ath11k_hw_ring_mask_qca6390,
++ .internal_sleep_clock = true,
++ .regs = &wcn6855_regs,
++ .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390,
++ .host_ce_config = ath11k_host_ce_config_qca6390,
++ .ce_count = 9,
++ .target_ce_config = ath11k_target_ce_config_wlan_qca6390,
++ .target_ce_count = 9,
++ .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390,
++ .svc_to_ce_map_len = 14,
++ .single_pdev_only = true,
++ .rxdma1_enable = false,
++ .num_rxmda_per_pdev = 2,
++ .rx_mac_buf_ring = true,
++ .vdev_start_delay = true,
++ .htt_peer_map_v2 = false,
++
++ .spectral = {
++ .fft_sz = 0,
++ .fft_pad_sz = 0,
++ .summary_pad_sz = 0,
++ .fft_hdr_len = 0,
++ .max_fft_bins = 0,
++ },
++
+ .interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP),
+ .supports_monitor = false,
+@@ -456,6 +536,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE,
+ .smp2p_wow_exit = false,
+ .support_fw_mac_sequence = true,
++ .coex_isolation = true,
+ },
+ {
+ .name = "wcn6855 hw2.1",
+@@ -497,6 +578,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP),
+ .supports_monitor = false,
++ .full_monitor_mode = false,
+ .supports_shadow_regs = true,
+ .idle_ps = true,
+ .supports_sta_ps = true,
+@@ -538,6 +620,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .tcl_ring_retry = true,
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE,
+ .smp2p_wow_exit = false,
++ .coex_isolation = false,
+ .support_fw_mac_sequence = true,
+ },
+ {
+@@ -619,6 +702,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ .tcl_ring_retry = false,
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750,
+ .smp2p_wow_exit = true,
++ .coex_isolation = false,
+ .support_fw_mac_sequence = true,
+ },
+ {
+@@ -704,6 +788,40 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
+ },
+ };
+
++static const struct dma_map_ops *ath11k_core_get_dma_ops(struct device *dev)
++{
++ if (dev->dma_ops)
++ return dev->dma_ops;
++ return NULL;
++}
++
++void *ath11k_core_dma_alloc_coherent(struct device *dev, size_t size,
++ dma_addr_t *dma_handle, gfp_t flag)
++{
++ unsigned long attrs = (flag & __GFP_NOWARN) ? DMA_ATTR_NO_WARN : 0;
++ const struct dma_map_ops *ops = ath11k_core_get_dma_ops(dev);
++ void *cpu_addr;
++
++ WARN_ON_ONCE(!dev->coherent_dma_mask);
++
++ /*
++ * DMA allocations can never be turned back into a page pointer, so
++ * requesting compound pages doesn't make sense (and can't even be
++ * supported at all by various backends).
++ */
++ if (WARN_ON_ONCE(flag & __GFP_COMP))
++ return NULL;
++
++ if (!ops)
++ cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
++ else if (ops->alloc)
++ cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
++ else
++ return NULL;
++
++ return cpu_addr;
++}
++
+ static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab)
+ {
+ WARN_ON(!ab->hw_params.single_pdev_only);
+@@ -1505,6 +1623,30 @@ static void ath11k_core_pdev_destroy(struct ath11k_base *ab)
+ ath11k_debugfs_pdev_destroy(ab);
+ }
+
++static int ath11k_core_config_coex_isolation(struct ath11k_base *ab)
++{
++ struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
++ struct wmi_coex_config_params param;
++
++ memset(&param, 0, sizeof(struct wmi_coex_config_params));
++ param.config_type = WMI_COEX_CONFIG_ANTENNA_ISOLATION;
++ param.config_arg1 = WMI_COEX_ISOLATION_ARG1_DEFAUT;
++
++ return ath11k_wmi_send_coex_config(ar, &param);
++}
++
++static int ath11k_core_config_btc_mode(struct ath11k_base *ab)
++{
++ struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
++ struct wmi_coex_config_params param;
++
++ memset(&param, 0, sizeof(struct wmi_coex_config_params));
++ param.config_type = WMI_COEX_CONFIG_BTC_MODE;
++ param.config_arg1 = WMI_COEX_BTC_MODE_ARG1_DEFAULT;
++
++ return ath11k_wmi_send_coex_config(ar, &param);
++}
++
+ static int ath11k_core_start(struct ath11k_base *ab)
+ {
+ int ret;
+@@ -1602,6 +1744,22 @@ static int ath11k_core_start(struct ath11k_base *ab)
+ goto err_reo_cleanup;
+ }
+
++ if (ab->hw_params.coex_isolation) {
++ ret = ath11k_core_config_coex_isolation(ab);
++ if (ret) {
++ ath11k_err(ab, "failed to set coex isolation: %d\n",
++ ret);
++ goto err_reo_cleanup;
++ }
++ }
++
++ ret = ath11k_core_config_btc_mode(ab);
++ if (ret) {
++ ath11k_err(ab, "failed to set btc mode: %d\n",
++ ret);
++ goto err_reo_cleanup;
++ }
++
+ return 0;
+
+ err_reo_cleanup:
+@@ -1751,6 +1909,7 @@ void ath11k_core_halt(struct ath11k *ar)
+ ath11k_mac_scan_finish(ar);
+ ath11k_mac_peer_cleanup_all(ar);
+ cancel_delayed_work_sync(&ar->scan.timeout);
++ cancel_work_sync(&ar->channel_update_work);
+ cancel_work_sync(&ar->regd_update_work);
+ cancel_work_sync(&ab->update_11d_work);
+
+diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
+index b04447762..bac6624f3 100644
+--- a/drivers/net/wireless/ath/ath11k/core.h
++++ b/drivers/net/wireless/ath/ath11k/core.h
+@@ -144,6 +144,7 @@ enum ath11k_hw_rev {
+ ATH11K_HW_WCN6855_HW21,
+ ATH11K_HW_WCN6750_HW10,
+ ATH11K_HW_IPQ5018_HW10,
++ ATH11K_HW_QCA206X_HW21,
+ };
+
+ enum ath11k_firmware_mode {
+@@ -311,6 +312,43 @@ struct ath11k_rekey_data {
+ bool enable_offload;
+ };
+
++/**
++ * struct chan_power_info - TPE containing power info per channel chunk
++ * @chan_cfreq: channel center freq (MHz)
++ * e.g.
++ * channel 37/20 MHz, it is 6135
++ * channel 37/40 MHz, it is 6125
++ * channel 37/80 MHz, it is 6145
++ * channel 37/160 MHz, it is 6185
++ * @tx_power: transmit power (dBm)
++ */
++struct chan_power_info {
++ u16 chan_cfreq;
++ s8 tx_power;
++};
++
++/**
++ * struct reg_tpc_power_info - regulatory TPC power info
++ * @is_psd_power: is PSD power or not
++ * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD
++ * @power_type_6g: type of power (SP/LPI/VLP)
++ * @num_pwr_levels: number of power levels
++ * @reg_max: Array of maximum TX power (dBm) per PSD value
++ * @ap_constraint_power: AP constraint power (dBm)
++ * @tpe: TPE values processed from TPE IE
++ * @chan_power_info: power info to send to firmware
++ */
++struct ath11k_reg_tpc_power_info {
++ bool is_psd_power;
++ u8 eirp_power;
++ enum wmi_reg_6ghz_ap_type power_type_6g;
++ u8 num_pwr_levels;
++ u8 reg_max[IEEE80211_MAX_NUM_PWR_LEVEL];
++ u8 ap_constraint_power;
++ s8 tpe[IEEE80211_MAX_NUM_PWR_LEVEL];
++ struct chan_power_info chan_power_info[IEEE80211_MAX_NUM_PWR_LEVEL];
++};
++
+ struct ath11k_vif {
+ u32 vdev_id;
+ enum wmi_vdev_type vdev_type;
+@@ -369,6 +407,7 @@ struct ath11k_vif {
+ #ifdef CONFIG_ATH11K_DEBUGFS
+ struct dentry *debugfs_twt;
+ #endif /* CONFIG_ATH11K_DEBUGFS */
++ struct ath11k_reg_tpc_power_info reg_tpc_info;
+ };
+
+ struct ath11k_vif_iter {
+@@ -697,6 +736,10 @@ struct ath11k {
+ struct completion bss_survey_done;
+
+ struct work_struct regd_update_work;
++ struct work_struct channel_update_work;
++ struct list_head channel_update_queue;
++ /* protects channel_update_queue data */
++ spinlock_t channel_update_lock;
+
+ struct work_struct wmi_mgmt_tx_work;
+ struct sk_buff_head wmi_mgmt_tx_queue;
+@@ -737,6 +780,7 @@ struct ath11k {
+ /* protected by conf_mutex */
+ bool ps_state_enable;
+ bool ps_timekeeper_enable;
++ s8 max_allowed_tx_power;
+ };
+
+ struct ath11k_band_cap {
+@@ -840,6 +884,16 @@ struct ath11k_msi_config {
+ u16 hw_rev;
+ };
+
++struct fw_remote_mem {
++ size_t size;
++ void *vaddr;
++};
++
++struct fw_remote_crash_data {
++ u8 *remote_buf;
++ size_t remote_buf_len;
++};
++
+ /* Master structure to hold the hw data which may be used in core module */
+ struct ath11k_base {
+ enum ath11k_hw_rev hw_rev;
+@@ -923,6 +977,7 @@ struct ath11k_base {
+ * This may or may not be used during the runtime
+ */
+ struct ieee80211_regdomain *new_regd[MAX_RADIOS];
++ struct cur_regulatory_info *reg_info_store;
+
+ /* Current DFS Regulatory */
+ enum ath11k_dfs_region dfs_region;
+@@ -992,6 +1047,9 @@ struct ath11k_base {
+ } testmode;
+ #endif
+
++ struct fw_remote_mem remote_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01];
++ struct fw_remote_crash_data remote_crash_data;
++
+ /* must be last */
+ u8 drv_priv[] __aligned(sizeof(void *));
+ };
+@@ -1251,4 +1309,6 @@ static inline const char *ath11k_bus_str(enum ath11k_bus bus)
+ return "unknown";
+ }
+
++void *ath11k_core_dma_alloc_coherent(struct device *dev, size_t size,
++ dma_addr_t *dma_handle, gfp_t flag);
+ #endif /* _CORE_H_ */
+diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c
+index 5bb6fd17f..7fad6e447 100644
+--- a/drivers/net/wireless/ath/ath11k/debugfs.c
++++ b/drivers/net/wireless/ath/ath11k/debugfs.c
+@@ -468,6 +468,78 @@ static const struct file_operations fops_bcn_stats = {
+ .llseek = default_llseek,
+ };
+
++static ssize_t ath11k_debugfs_write_coex_config(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath11k_base *ab = file->private_data;
++ struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
++ char buf[64] = {0};
++ ssize_t ret;
++ int rc;
++ u32 config_type, config_arg1;
++ char sep[] = " ";
++ char *token, *cur;
++ struct wmi_coex_config_params param;
++
++ if (*ppos != 0 || count >= sizeof(buf) || count == 0) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
++ if (ret < 0)
++ goto out;
++
++ /* drop the possible '\n' from the end */
++ if (buf[*ppos - 1] == '\n')
++ buf[*ppos - 1] = '\0';
++
++ ath11k_info(ab, "%s: %s\n", __func__, buf);
++ ret = count;
++ cur = buf;
++
++ token = strsep(&cur, sep);
++ rc = kstrtou32(token, 0, &config_type);
++ if (rc) {
++ ath11k_warn(ab, "%s convert error: config_type %s\n", __func__, token);
++ ret = -EFAULT;
++ goto out;
++ }
++
++ token = strim(cur);
++ rc = kstrtou32(token, 0, &config_arg1);
++ if (rc) {
++ ath11k_warn(ab, "%s convert error: config_arg1 %s\n", __func__, token);
++ ret = -EFAULT;
++ goto out;
++ }
++
++
++ memset(&param, 0, sizeof(struct wmi_coex_config_params));
++ param.config_type = config_type;
++ param.config_arg1 = config_arg1;
++
++ mutex_lock(&ar->conf_mutex);
++
++ rc = ath11k_mac_send_coex_config(ar, &param);
++ if (rc) {
++ ath11k_warn(ab, "failed to send coex config using debugfs %d\n", rc);
++ ret = -EFAULT;
++ }
++
++ mutex_unlock(&ar->conf_mutex);
++out:
++ return ret;
++}
++
++static const struct file_operations fops_coex_config = {
++ .write = ath11k_debugfs_write_coex_config,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
+ static ssize_t ath11k_read_simulate_fw_crash(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+@@ -532,6 +604,10 @@ static ssize_t ath11k_write_simulate_fw_crash(struct file *file,
+ ath11k_info(ab, "user requested hw restart\n");
+ queue_work(ab->workqueue_aux, &ab->reset_work);
+ ret = 0;
++ } else if (!strcmp(buf, "mhi-rddm")) {
++ ath11k_info(ab, "force target rddm\n");
++ ath11k_hif_force_rddm(ab);
++ ret = 0;
+ } else {
+ ret = -EINVAL;
+ goto exit;
+@@ -957,9 +1033,12 @@ static ssize_t ath11k_read_sram_dump(struct file *file,
+
+ static int ath11k_release_sram_dump(struct inode *inode, struct file *file)
+ {
++ struct ath11k_base *ab = inode->i_private;
+ vfree(file->private_data);
+ file->private_data = NULL;
+
++ debugfs_create_file("coex_config", 0600, ab->debugfs_soc, ab,
++ &fops_coex_config);
+ return 0;
+ }
+
+diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
+index d070bcb3f..6b6d0ce4f 100644
+--- a/drivers/net/wireless/ath/ath11k/dp.c
++++ b/drivers/net/wireless/ath/ath11k/dp.c
+@@ -254,9 +254,9 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
+ }
+
+ if (!cached)
+- ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size,
++ ring->vaddr_unaligned = ath11k_core_dma_alloc_coherent(ab->dev, ring->size,
+ &ring->paddr_unaligned,
+- GFP_KERNEL);
++ GFP_KERNEL | GFP_DMA32);
+
+ if (!ring->vaddr_unaligned)
+ return -ENOMEM;
+@@ -527,9 +527,9 @@ static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab,
+ return -EINVAL;
+
+ for (i = 0; i < num_scatter_buf; i++) {
+- slist[i].vaddr = dma_alloc_coherent(ab->dev,
++ slist[i].vaddr = ath11k_core_dma_alloc_coherent(ab->dev,
+ HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX,
+- &slist[i].paddr, GFP_KERNEL);
++ &slist[i].paddr, GFP_KERNEL | GFP_DMA32);
+ if (!slist[i].vaddr) {
+ ret = -ENOMEM;
+ goto err;
+@@ -607,9 +607,9 @@ static int ath11k_dp_link_desc_bank_alloc(struct ath11k_base *ab,
+ desc_sz = last_bank_sz;
+
+ desc_bank[i].vaddr_unaligned =
+- dma_alloc_coherent(ab->dev, desc_sz,
++ ath11k_core_dma_alloc_coherent(ab->dev, desc_sz,
+ &desc_bank[i].paddr_unaligned,
+- GFP_KERNEL);
++ GFP_KERNEL | GFP_DMA32);
+ if (!desc_bank[i].vaddr_unaligned) {
+ ret = -ENOMEM;
+ goto err;
+diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
+index a993e74bb..5b1320b4b 100644
+--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
+@@ -2549,7 +2549,7 @@ static int ath11k_dp_rx_process_msdu(struct ath11k *ar,
+ lrx_desc = (struct hal_rx_desc *)last_buf->data;
+ rx_attention = ath11k_dp_rx_get_attention(ab, lrx_desc);
+ if (!ath11k_dp_rx_h_attn_msdu_done(rx_attention)) {
+- ath11k_warn(ab, "msdu_done bit in attention is not set\n");
++ ath11k_dbg(ab, ATH11K_DBG_DATA, "msdu_done bit in attention is not set\n");
+ ret = -EIO;
+ goto free_out;
+ }
+diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c
+index 0a99aa7dd..efff993c9 100644
+--- a/drivers/net/wireless/ath/ath11k/hal.c
++++ b/drivers/net/wireless/ath/ath11k/hal.c
+@@ -8,6 +8,7 @@
+ #include "debug.h"
+ #include "hal_desc.h"
+ #include "hif.h"
++#include "core.h"
+
+ static const struct hal_srng_config hw_srng_config_template[] = {
+ /* TODO: max_rings can populated by querying HW capabilities */
+@@ -196,8 +197,8 @@ static int ath11k_hal_alloc_cont_rdp(struct ath11k_base *ab)
+ size_t size;
+
+ size = sizeof(u32) * HAL_SRNG_RING_ID_MAX;
+- hal->rdp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->rdp.paddr,
+- GFP_KERNEL);
++ hal->rdp.vaddr = ath11k_core_dma_alloc_coherent(ab->dev, size, &hal->rdp.paddr,
++ GFP_KERNEL | GFP_DMA32);
+ if (!hal->rdp.vaddr)
+ return -ENOMEM;
+
+@@ -224,8 +225,8 @@ static int ath11k_hal_alloc_cont_wrp(struct ath11k_base *ab)
+ size_t size;
+
+ size = sizeof(u32) * HAL_SRNG_NUM_LMAC_RINGS;
+- hal->wrp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->wrp.paddr,
+- GFP_KERNEL);
++ hal->wrp.vaddr = ath11k_core_dma_alloc_coherent(ab->dev, size, &hal->wrp.paddr,
++ GFP_KERNEL | GFP_DMA32);
+ if (!hal->wrp.vaddr)
+ return -ENOMEM;
+
+diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h
+index 659b80d2a..7eca43a91 100644
+--- a/drivers/net/wireless/ath/ath11k/hif.h
++++ b/drivers/net/wireless/ath/ath11k/hif.h
+@@ -30,6 +30,7 @@ struct ath11k_hif_ops {
+ void (*ce_irq_enable)(struct ath11k_base *ab);
+ void (*ce_irq_disable)(struct ath11k_base *ab);
+ void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
++ int (*target_crash)(struct ath11k_base *ab);
+ };
+
+ static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab)
+@@ -90,6 +91,13 @@ static inline int ath11k_hif_resume(struct ath11k_base *ab)
+ return 0;
+ }
+
++static inline int ath11k_hif_force_rddm(struct ath11k_base *ab)
++{
++ if (ab->hif.ops->target_crash)
++ return ab->hif.ops->target_crash(ab);
++ return 0;
++}
++
+ static inline u32 ath11k_hif_read32(struct ath11k_base *sc, u32 address)
+ {
+ return sc->hif.ops->read32(sc, address);
+diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c
+index d7b5ec6e6..1e82683f9 100644
+--- a/drivers/net/wireless/ath/ath11k/hw.c
++++ b/drivers/net/wireless/ath/ath11k/hw.c
+@@ -100,6 +100,7 @@ static void ath11k_init_wmi_config_qca6390(struct ath11k_base *ab,
+ config->num_wow_filters = 0x16;
+ config->num_keep_alive_pattern = 0;
+ config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64;
++ config->host_service_flags |= WMI_RSRC_CFG_HOST_SERVICE_FLAG_NAN_IFACE_SUPPORT;
+ }
+
+ static void ath11k_hw_ipq8074_reo_setup(struct ath11k_base *ab)
+@@ -900,6 +901,18 @@ static u32 ath11k_hw_wcn6750_get_tcl_ring_selector(struct sk_buff *skb)
+ return skb_get_hash(skb);
+ }
+
++bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar)
++{
++ return (test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT,
++ ar->ab->wmi_ab.svc_map)) && ar->supports_6ghz;
++}
++
++bool ath11k_hw_supports_tpc_ext(struct ath11k *ar)
++{
++ return ath11k_hw_supports_6g_cc_ext(ar) &&
++ test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map);
++}
++
+ const struct ath11k_hw_ops ipq8074_ops = {
+ .get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id,
+ .wmi_init_config = ath11k_init_wmi_config_ipq8074,
+diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
+index d51a99669..b23494409 100644
+--- a/drivers/net/wireless/ath/ath11k/hw.h
++++ b/drivers/net/wireless/ath/ath11k/hw.h
+@@ -225,6 +225,11 @@ struct ath11k_hw_params {
+ bool tcl_ring_retry;
+ u32 tx_ring_size;
+ bool smp2p_wow_exit;
++ bool wakeup_mhi;
++ u32 rfkill_pin;
++ u32 rfkill_cfg;
++ u32 rfkill_on_level;
++ bool coex_isolation;
+ bool support_fw_mac_sequence;
+ };
+
+@@ -321,6 +326,9 @@ static inline int ath11k_hw_mac_id_to_srng_id(struct ath11k_hw_params *hw,
+ return 0;
+ }
+
++bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar);
++bool ath11k_hw_supports_tpc_ext(struct ath11k *ar);
++
+ struct ath11k_fw_ie {
+ __le32 id;
+ __le32 len;
+diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
+index b328a0599..8a034a971 100644
+--- a/drivers/net/wireless/ath/ath11k/mac.c
++++ b/drivers/net/wireless/ath/ath11k/mac.c
+@@ -23,6 +23,7 @@
+ #include "debugfs_sta.h"
+ #include "hif.h"
+ #include "wow.h"
++#include "unitest.h"
+
+ #define CHAN2G(_channel, _freq, _flags) { \
+ .band = NL80211_BAND_2GHZ, \
+@@ -254,6 +255,10 @@ static const u32 ath11k_smps_map[] = {
+ [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE,
+ };
+
++static struct wiphy_vendor_command ath11k_vendor_cmds[] = {
++ ath11k_unit_test_command,
++};
++
+ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+@@ -629,6 +634,17 @@ struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id)
+ return NULL;
+ }
+
++enum wmi_vdev_type ath11k_mac_get_ar_vdev_type(struct ath11k *ar)
++{
++ struct ath11k_vif *arvif;
++
++ list_for_each_entry(arvif, &ar->arvifs, list) {
++ return arvif->vdev_type;
++ }
++
++ return WMI_VDEV_TYPE_UNSPEC;
++}
++
+ struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id)
+ {
+ int i;
+@@ -2980,6 +2996,27 @@ static bool ath11k_mac_vif_recalc_sta_he_txbf(struct ath11k *ar,
+ hemode |= FIELD_PREP(HE_MODE_SU_TX_BFER, HE_SU_BFER_ENABLE);
+ }
+
++ ath11k_info(ar->ab, "mac0-5 cap %x-%x-%x-%x-%x-%x\n",
++ he_cap_elem.mac_cap_info[0],
++ he_cap_elem.mac_cap_info[1],
++ he_cap_elem.mac_cap_info[2],
++ he_cap_elem.mac_cap_info[3],
++ he_cap_elem.mac_cap_info[4],
++ he_cap_elem.mac_cap_info[5]);
++ ath11k_info(ar->ab, "phy0-5 cap %x-%x-%x-%x-%x-%x\n",
++ he_cap_elem.phy_cap_info[0],
++ he_cap_elem.phy_cap_info[1],
++ he_cap_elem.phy_cap_info[2],
++ he_cap_elem.phy_cap_info[3],
++ he_cap_elem.phy_cap_info[4],
++ he_cap_elem.phy_cap_info[5]);
++ ath11k_info(ar->ab, "phy6-10 cap %x-%x-%x-%x-%x\n",
++ he_cap_elem.phy_cap_info[6],
++ he_cap_elem.phy_cap_info[7],
++ he_cap_elem.phy_cap_info[8],
++ he_cap_elem.phy_cap_info[9],
++ he_cap_elem.phy_cap_info[10]);
++ ath11k_info(ar->ab, "WMI_VDEV_PARAM_SET_HEMU_MODE 3 0x%x\n", hemode);
+ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, hemode);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to submit vdev param txbf 0x%x: %d\n",
+@@ -3025,7 +3062,14 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw,
+
+ rcu_read_unlock();
+
++ if (!ath11k_mac_vif_recalc_sta_he_txbf(ar, vif, &he_cap)) {
++ ath11k_warn(ar->ab, "failed to recalc he txbf for vdev %i on bss %pM\n",
++ arvif->vdev_id, bss_conf->bssid);
++ return;
++ }
++
+ peer_arg.is_assoc = true;
++
+ ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
+@@ -3048,12 +3092,6 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw,
+ return;
+ }
+
+- if (!ath11k_mac_vif_recalc_sta_he_txbf(ar, vif, &he_cap)) {
+- ath11k_warn(ar->ab, "failed to recalc he txbf for vdev %i on bss %pM\n",
+- arvif->vdev_id, bss_conf->bssid);
+- return;
+- }
+-
+ WARN_ON(arvif->is_up);
+
+ arvif->aid = vif->cfg.aid;
+@@ -3396,6 +3434,16 @@ static int ath11k_mac_config_obss_pd(struct ath11k *ar,
+ return 0;
+ }
+
++static bool ath11k_mac_supports_station_tpc(struct ath11k *ar,
++ struct ath11k_vif *arvif,
++ const struct cfg80211_chan_def *chandef)
++{
++ return ath11k_hw_supports_tpc_ext(ar) &&
++ arvif->vdev_type == WMI_VDEV_TYPE_STA &&
++ chandef->chan &&
++ chandef->chan->band == NL80211_BAND_6GHZ;
++}
++
+ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+@@ -3596,8 +3644,13 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev_id %i txpower %d\n",
+ arvif->vdev_id, info->txpower);
+
+- arvif->txpower = info->txpower;
+- ath11k_mac_txpower_recalc(ar);
++ if (ath11k_mac_supports_station_tpc(ar, arvif, &info->chandef)) {
++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
++ "discard tx power, change to set TPC power\n");
++ } else {
++ arvif->txpower = info->txpower;
++ ath11k_mac_txpower_recalc(ar);
++ }
+ }
+
+ if (changed & BSS_CHANGED_PS &&
+@@ -5463,8 +5516,6 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif)
+ if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) {
+ nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
+ nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+- if (nsts > (ar->num_rx_chains - 1))
+- nsts = ar->num_rx_chains - 1;
+ value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET);
+ }
+
+@@ -5505,7 +5556,7 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif)
+ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
+ {
+ bool subfer, subfee;
+- int sound_dim = 0, nsts = 0;
++ int sound_dim = 0;
+
+ subfer = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE));
+ subfee = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE));
+@@ -5515,11 +5566,6 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
+ subfer = false;
+ }
+
+- if (ar->num_rx_chains < 2) {
+- *vht_cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+- subfee = false;
+- }
+-
+ /* If SU Beaformer is not set, then disable MU Beamformer Capability */
+ if (!subfer)
+ *vht_cap &= ~(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
+@@ -5532,9 +5578,7 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
+ sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+ *vht_cap &= ~IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
+
+- nsts = (*vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK);
+- nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+- *vht_cap &= ~IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
++ /* TODO: Need to check invalid STS and Sound_dim values set by FW? */
+
+ /* Enable Sounding Dimension Field only if SU BF is enabled */
+ if (subfer) {
+@@ -5546,15 +5590,9 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
+ *vht_cap |= sound_dim;
+ }
+
+- /* Enable Beamformee STS Field only if SU BF is enabled */
+- if (subfee) {
+- if (nsts > (ar->num_rx_chains - 1))
+- nsts = ar->num_rx_chains - 1;
+-
+- nsts <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+- nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
+- *vht_cap |= nsts;
+- }
++ /* Use the STS advertised by FW unless SU Beamformee is not supported*/
++ if (!subfee)
++ *vht_cap &= ~(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK);
+ }
+
+ static struct ieee80211_sta_vht_cap
+@@ -6386,6 +6424,14 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw)
+ }
+ }
+
++ if (ath11k_hw_supports_6g_cc_ext(ar)) {
++ struct cur_regulatory_info *reg_info;
++
++ reg_info = &ab->reg_info_store[ar->pdev_idx];
++ ath11k_dbg(ab, ATH11K_DBG_MAC, "mac interface added to change reg rules\n");
++ ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_LPI_AP);
++ }
++
+ mutex_unlock(&ar->conf_mutex);
+
+ rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx],
+@@ -6404,6 +6450,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw)
+ {
+ struct ath11k *ar = hw->priv;
+ struct htt_ppdu_stats_info *ppdu_stats, *tmp;
++ struct scan_chan_list_params *params, *tmp_ch;
+ int ret;
+
+ ath11k_mac_drain_tx(ar);
+@@ -6419,6 +6466,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw)
+ mutex_unlock(&ar->conf_mutex);
+
+ cancel_delayed_work_sync(&ar->scan.timeout);
++ cancel_work_sync(&ar->channel_update_work);
+ cancel_work_sync(&ar->regd_update_work);
+ cancel_work_sync(&ar->ab->update_11d_work);
+
+@@ -6434,6 +6482,13 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw)
+ }
+ spin_unlock_bh(&ar->data_lock);
+
++ spin_lock_bh(&ar->channel_update_lock);
++ list_for_each_entry_safe(params, tmp_ch, &ar->channel_update_queue, list) {
++ list_del(&params->list);
++ kfree(params);
++ }
++ spin_unlock_bh(&ar->channel_update_lock);
++
+ rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL);
+
+ synchronize_rcu();
+@@ -7266,6 +7321,12 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif,
+ return ret;
+ }
+
++ if (ath11k_mac_supports_station_tpc(ar, arvif, chandef)) {
++ ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, &arvif->chanctx);
++ ath11k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id,
++ &arvif->reg_tpc_info);
++ }
++
+ if (!restart)
+ ar->num_started_vdevs++;
+
+@@ -7588,6 +7649,469 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw,
+ return 0;
+ }
+
++static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt)
++{
++ switch (txpwr_intrprt) {
++ /* Refer "Table 9-276-Meaning of Maximum Transmit Power Count subfield
++ * if the Maximum Transmit Power Interpretation subfield is 0 or 2" of
++ * "IEEE Std 802.11ax 2021".
++ */
++ case IEEE80211_TPE_LOCAL_EIRP:
++ case IEEE80211_TPE_REG_CLIENT_EIRP:
++ txpwr_cnt = txpwr_cnt <= 3 ? txpwr_cnt : 3;
++ txpwr_cnt = txpwr_cnt + 1;
++ break;
++ /* Refer "Table 9-277-Meaning of Maximum Transmit Power Count subfield
++ * if Maximum Transmit Power Interpretation subfield is 1 or 3" of
++ * "IEEE Std 802.11ax 2021".
++ */
++ case IEEE80211_TPE_LOCAL_EIRP_PSD:
++ case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:
++ txpwr_cnt = txpwr_cnt <= 4 ? txpwr_cnt : 4;
++ txpwr_cnt = txpwr_cnt ? (BIT(txpwr_cnt - 1)) : 1;
++ break;
++ }
++
++ return txpwr_cnt;
++}
++
++static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
++{
++ if (chan_def->chan->flags & IEEE80211_CHAN_PSD) {
++ switch (chan_def->width) {
++ case NL80211_CHAN_WIDTH_20:
++ return 1;
++ case NL80211_CHAN_WIDTH_40:
++ return 2;
++ case NL80211_CHAN_WIDTH_80:
++ return 4;
++ case NL80211_CHAN_WIDTH_80P80:
++ case NL80211_CHAN_WIDTH_160:
++ return 8;
++ default:
++ return 1;
++ }
++ } else {
++ switch (chan_def->width) {
++ case NL80211_CHAN_WIDTH_20:
++ return 1;
++ case NL80211_CHAN_WIDTH_40:
++ return 2;
++ case NL80211_CHAN_WIDTH_80:
++ return 3;
++ case NL80211_CHAN_WIDTH_80P80:
++ case NL80211_CHAN_WIDTH_160:
++ return 4;
++ default:
++ return 1;
++ }
++ }
++}
++
++static u16 ath11k_mac_get_6g_start_frequency(struct cfg80211_chan_def *chan_def)
++{
++ u16 diff_seq;
++
++ /* It is to get the lowest channel number's center frequency of the chan.
++ * For example,
++ * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1
++ * with center frequency 5955, its diff is 5965 - 5955 = 10.
++ * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1
++ * with center frequency 5955, its diff is 5985 - 5955 = 30.
++ * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1
++ * with center frequency 5955, its diff is 6025 - 5955 = 70.
++ */
++ switch (chan_def->width) {
++ case NL80211_CHAN_WIDTH_160:
++ diff_seq = 70;
++ break;
++ case NL80211_CHAN_WIDTH_80:
++ case NL80211_CHAN_WIDTH_80P80:
++ diff_seq = 30;
++ break;
++ case NL80211_CHAN_WIDTH_40:
++ diff_seq = 10;
++ break;
++ default:
++ diff_seq = 0;
++ }
++
++ return chan_def->center_freq1 - diff_seq;
++}
++
++static u16 ath11k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def,
++ u16 start_seq, u8 seq)
++{
++ u16 seg_seq;
++
++ /* It is to get the center frequency of the specific bandwidth.
++ * start_seq means the lowest channel number's center frequency.
++ * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz&80P80.
++ * For example,
++ * lowest channel is 1, its center frequency 5955,
++ * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0.
++ * lowest channel is 1, its center frequency 5955,
++ * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10.
++ * lowest channel is 1, its center frequency 5955,
++ * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30.
++ * lowest channel is 1, its center frequency 5955,
++ * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70.
++ */
++ if (chan_def->width == NL80211_CHAN_WIDTH_80P80 && seq == 3)
++ return chan_def->center_freq2;
++
++ seg_seq = 10 * (BIT(seq) - 1);
++ return seg_seq + start_seq;
++}
++
++static void ath11k_mac_get_psd_channel(struct ath11k *ar,
++ u16 step_freq,
++ u16 *start_freq,
++ u16 *center_freq,
++ u8 i,
++ struct ieee80211_channel **temp_chan,
++ s8 *tx_power)
++{
++ /* It is to get the the center frequency for each 20 MHz.
++ * For example, if the chan is 160 MHz and center frequency is 6025,
++ * then it include 8 channels, they are 1/5/9/13/17/21/25/29,
++ * channel number 1's center frequency is 5955, it is parameter start_freq.
++ * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels.
++ * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7,
++ * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095,
++ * the gap is 20 for each channel, parameter step_freq means the gap.
++ * after get the center frequency of each channel, it is easy to find the
++ * struct ieee80211_channel of it and get the max_reg_power.
++ */
++ *center_freq = *start_freq + i * step_freq;
++ *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
++ *tx_power = (*temp_chan)->max_reg_power;
++}
++
++static void ath11k_mac_get_eirp_power(struct ath11k *ar,
++ u16 *start_freq,
++ u16 *center_freq,
++ u8 i,
++ struct ieee80211_channel **temp_chan,
++ struct cfg80211_chan_def *def,
++ s8 *tx_power)
++{
++ /* It is to get the the center frequency for 20 MHz/40 MHz/80 MHz/
++ * 160 MHz&80P80 bandwidth, and then plus 10 to the center frequency,
++ * it is the center frequency of a channel number.
++ * For example, when configured channel number is 1.
++ * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975,
++ * then it is channel number 5.
++ * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995,
++ * then it is channel number 9.
++ * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035,
++ * then it is channel number 17.
++ * after get the center frequency of each channel, it is easy to find the
++ * struct ieee80211_channel of it and get the max_reg_power.
++ */
++ *center_freq = ath11k_mac_get_seg_freq(def, *start_freq, i);
++
++ /* For the 20 MHz, its center frequency is same with same channel */
++ if (i != 0)
++ *center_freq += 10;
++
++ *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
++ *tx_power = (*temp_chan)->max_reg_power;
++}
++
++void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
++ struct ieee80211_vif *vif,
++ struct ieee80211_chanctx_conf *ctx)
++{
++ struct ath11k_base *ab = ar->ab;
++ struct ath11k_vif *arvif = (void *)vif->drv_priv;
++ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
++ struct ath11k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info;
++ struct ieee80211_channel *chan, *temp_chan;
++ u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction;
++ bool is_psd_power = false, is_tpe_present = false;
++ s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL],
++ psd_power, tx_power, eirp_power;
++ u16 oper_freq, start_freq, center_freq;
++
++ chan = ctx->def.chan;
++ oper_freq = ctx->def.chan->center_freq;
++ start_freq = ath11k_mac_get_6g_start_frequency(&ctx->def);
++ pwr_reduction = bss_conf->pwr_reduction;
++
++ if (arvif->reg_tpc_info.num_pwr_levels) {
++ is_tpe_present = true;
++ num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels;
++ } else {
++ num_pwr_levels = ath11k_mac_get_num_pwr_levels(&ctx->def);
++ }
++
++ for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) {
++ /* STA received TPE IE*/
++ if (is_tpe_present) {
++ /* local power is PSD power*/
++ if (chan->flags & IEEE80211_CHAN_PSD) {
++ /* Connecting AP is psd power */
++ if (reg_tpc_info->is_psd_power) {
++ is_psd_power = true;
++ ath11k_mac_get_psd_channel(ar, 20,
++ &start_freq,
++ &center_freq,
++ pwr_lvl_idx,
++ &temp_chan,
++ &tx_power);
++ psd_power = temp_chan->psd;
++ eirp_power = tx_power;
++ max_tx_power[pwr_lvl_idx] =
++ min_t(s8,
++ psd_power,
++ reg_tpc_info->tpe[pwr_lvl_idx]);
++ /* Connecting AP is not psd power */
++ } else {
++ ath11k_mac_get_eirp_power(ar,
++ &start_freq,
++ &center_freq,
++ pwr_lvl_idx,
++ &temp_chan,
++ &ctx->def,
++ &tx_power);
++ psd_power = temp_chan->psd;
++ /* convert psd power to EIRP power based
++ * on channel width
++ */
++ tx_power =
++ min_t(s8, tx_power,
++ psd_power + 13 + pwr_lvl_idx * 3);
++ max_tx_power[pwr_lvl_idx] =
++ min_t(s8,
++ tx_power,
++ reg_tpc_info->tpe[pwr_lvl_idx]);
++ }
++ /* local power is not PSD power */
++ } else {
++ /* Connecting AP is psd power */
++ if (reg_tpc_info->is_psd_power) {
++ is_psd_power = true;
++ ath11k_mac_get_psd_channel(ar, 20,
++ &start_freq,
++ &center_freq,
++ pwr_lvl_idx,
++ &temp_chan,
++ &tx_power);
++ eirp_power = tx_power;
++ max_tx_power[pwr_lvl_idx] =
++ reg_tpc_info->tpe[pwr_lvl_idx];
++ /* Connecting AP is not psd power */
++ } else {
++ ath11k_mac_get_eirp_power(ar,
++ &start_freq,
++ &center_freq,
++ pwr_lvl_idx,
++ &temp_chan,
++ &ctx->def,
++ &tx_power);
++ max_tx_power[pwr_lvl_idx] =
++ min_t(s8,
++ tx_power,
++ reg_tpc_info->tpe[pwr_lvl_idx]);
++ }
++ }
++ /* STA not received TPE IE */
++ } else {
++ /* local power is PSD power*/
++ if (chan->flags & IEEE80211_CHAN_PSD) {
++ is_psd_power = true;
++ ath11k_mac_get_psd_channel(ar, 20,
++ &start_freq,
++ &center_freq,
++ pwr_lvl_idx,
++ &temp_chan,
++ &tx_power);
++ psd_power = temp_chan->psd;
++ eirp_power = tx_power;
++ max_tx_power[pwr_lvl_idx] = psd_power;
++ } else {
++ ath11k_mac_get_eirp_power(ar,
++ &start_freq,
++ &center_freq,
++ pwr_lvl_idx,
++ &temp_chan,
++ &ctx->def,
++ &tx_power);
++ max_tx_power[pwr_lvl_idx] =
++ min_t(s8,
++ tx_power,
++ reg_tpc_info->tpe[pwr_lvl_idx]);
++ }
++ }
++
++ if (is_psd_power) {
++ /* If AP local power constraint is present */
++ if (pwr_reduction)
++ eirp_power = eirp_power - pwr_reduction;
++
++ /* If firmware updated max tx power is non zero, then take
++ * the min of firmware updated ap tx power
++ * and max power derived from above mentioned parameters.
++ */
++ ath11k_dbg(ab, ATH11K_DBG_MAC,
++ "eirp power : %d firmware report power : %d\n",
++ eirp_power, ar->max_allowed_tx_power);
++ if (ar->max_allowed_tx_power)
++ eirp_power = min_t(s8,
++ eirp_power,
++ ar->max_allowed_tx_power);
++ } else {
++ /* If AP local power constraint is present */
++ if (pwr_reduction)
++ max_tx_power[pwr_lvl_idx] =
++ max_tx_power[pwr_lvl_idx] - pwr_reduction;
++ /* If firmware updated max tx power is non zero, then take
++ * the min of firmware updated ap tx power
++ * and max power derived from above mentioned parameters.
++ */
++ if (ar->max_allowed_tx_power)
++ max_tx_power[pwr_lvl_idx] =
++ min_t(s8,
++ max_tx_power[pwr_lvl_idx],
++ ar->max_allowed_tx_power);
++ }
++ reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq;
++ reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power =
++ max_tx_power[pwr_lvl_idx];
++ }
++
++ reg_tpc_info->num_pwr_levels = num_pwr_levels;
++ reg_tpc_info->is_psd_power = is_psd_power;
++ reg_tpc_info->eirp_power = eirp_power;
++ reg_tpc_info->power_type_6g =
++ ath11k_ieee80211_ap_pwr_type_convert(vif->bss_conf.power_type);
++}
++
++static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar,
++ struct ieee80211_vif *vif,
++ struct ieee80211_chanctx_conf *ctx)
++{
++ struct ath11k_base *ab = ar->ab;
++ struct ath11k_vif *arvif = (void *)vif->drv_priv;
++ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
++ struct ieee80211_tx_pwr_env *single_tpe;
++ enum wmi_reg_6ghz_client_type client_type;
++ struct cur_regulatory_info *reg_info;
++ int i;
++ u8 pwr_count, pwr_interpret, pwr_category;
++ u8 psd_index = 0, non_psd_index = 0, local_tpe_count = 0, reg_tpe_count = 0;
++ bool use_local_tpe, non_psd_set = false, psd_set = false;
++
++ reg_info = &ab->reg_info_store[ar->pdev_idx];
++ client_type = reg_info->client_type;
++
++ for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
++ single_tpe = &bss_conf->tx_pwr_env[i];
++ pwr_category = u8_get_bits(single_tpe->tx_power_info,
++ IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
++ pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
++ IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
++
++ if (pwr_category == client_type) {
++ if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP ||
++ pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD)
++ local_tpe_count++;
++ else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP ||
++ pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD)
++ reg_tpe_count++;
++ }
++ }
++
++ if (!reg_tpe_count && !local_tpe_count) {
++ ath11k_warn(ab,
++ "no transmit power envelope match client power type %d\n",
++ client_type);
++ return;
++ } else if (!reg_tpe_count) {
++ use_local_tpe = true;
++ } else {
++ use_local_tpe = false;
++ }
++
++ for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
++ single_tpe = &bss_conf->tx_pwr_env[i];
++ pwr_category = u8_get_bits(single_tpe->tx_power_info,
++ IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
++ pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
++ IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
++
++ if (pwr_category != client_type)
++ continue;
++
++ /* get local transmit power envelope */
++ if (use_local_tpe) {
++ if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP) {
++ non_psd_index = i;
++ non_psd_set = true;
++ } else if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) {
++ psd_index = i;
++ psd_set = true;
++ }
++ /* get regulatory transmit power envelope */
++ } else {
++ if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP) {
++ non_psd_index = i;
++ non_psd_set = true;
++ } else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) {
++ psd_index = i;
++ psd_set = true;
++ }
++ }
++ }
++
++ if (non_psd_set && !psd_set) {
++ single_tpe = &bss_conf->tx_pwr_env[non_psd_index];
++ pwr_count = u8_get_bits(single_tpe->tx_power_info,
++ IEEE80211_TX_PWR_ENV_INFO_COUNT);
++ pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
++ IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
++ arvif->reg_tpc_info.is_psd_power = false;
++ arvif->reg_tpc_info.eirp_power = 0;
++
++ arvif->reg_tpc_info.num_pwr_levels =
++ ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
++ for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
++ ath11k_dbg(ab, ATH11K_DBG_MAC,
++ "non PSD power[%d] : %d\n",
++ i, single_tpe->tx_power[i]);
++ arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
++ }
++ }
++
++ if (psd_set) {
++ single_tpe = &bss_conf->tx_pwr_env[psd_index];
++ pwr_count = u8_get_bits(single_tpe->tx_power_info,
++ IEEE80211_TX_PWR_ENV_INFO_COUNT);
++ pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
++ IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
++ arvif->reg_tpc_info.is_psd_power = true;
++
++ if (pwr_count == 0) {
++ ath11k_dbg(ab, ATH11K_DBG_MAC,
++ "TPE PSD power : %d\n", single_tpe->tx_power[0]);
++ arvif->reg_tpc_info.num_pwr_levels =
++ ath11k_mac_get_num_pwr_levels(&ctx->def);
++ for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++)
++ arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[0] / 2;
++ } else {
++ arvif->reg_tpc_info.num_pwr_levels =
++ ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
++ for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
++ ath11k_dbg(ab, ATH11K_DBG_MAC,
++ "TPE PSD power[%d] : %d\n",
++ i, single_tpe->tx_power[i]);
++ arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
++ }
++ }
++ }
++}
++
+ static int
+ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+@@ -7599,6 +8123,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+ int ret;
+ struct peer_create_params param;
++ struct cur_regulatory_info *reg_info;
++ enum ieee80211_ap_reg_power power_type;
+
+ mutex_lock(&ar->conf_mutex);
+
+@@ -7606,6 +8132,20 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+ "chanctx assign ptr %p vdev_id %i\n",
+ ctx, arvif->vdev_id);
+
++ if (ath11k_hw_supports_6g_cc_ext(ar) &&
++ ctx->def.chan->band == NL80211_BAND_6GHZ &&
++ arvif->vdev_type == WMI_VDEV_TYPE_STA) {
++ reg_info = &ab->reg_info_store[ar->pdev_idx];
++ power_type = vif->bss_conf.power_type;
++ ath11k_dbg(ab, ATH11K_DBG_MAC, "mac chanctx power type %d\n",
++ power_type);
++ if (power_type == IEEE80211_REG_UNSET_AP)
++ power_type = IEEE80211_REG_LPI_AP;
++ ath11k_reg_handle_chan_list(ab, reg_info, power_type);
++ arvif->chanctx = *ctx;
++ ath11k_mac_parse_tx_pwr_env(ar, vif, ctx);
++ }
++
+ /* for QCA6390 bss peer must be created before vdev_start */
+ if (ab->hw_params.vdev_start_delay &&
+ arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+@@ -9276,6 +9816,31 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar,
+ return 0;
+ }
+
++static void ath11k_mac_setup_mac_address_list(struct ath11k *ar)
++{
++ struct mac_address *addresses;
++ u16 n_addresses;
++ int i;
++
++ if (!ar->ab->hw_params.single_pdev_only || ar->ab->hw_params.num_rxmda_per_pdev < 2)
++ return;
++
++ n_addresses = 3;
++ addresses = kcalloc(n_addresses, sizeof(*addresses), GFP_KERNEL);
++ if (!addresses)
++ return;
++
++ memcpy(addresses[0].addr, ar->mac_addr, ETH_ALEN);
++ for (i = 1; i < n_addresses; i++) {
++ memcpy(addresses[i].addr, ar->mac_addr, ETH_ALEN);
++ addresses[i].addr[0] |= 0x2;
++ addresses[i].addr[0] += (i - 1) << 4;
++ }
++
++ ar->hw->wiphy->addresses = addresses;
++ ar->hw->wiphy->n_addresses = n_addresses;
++}
++
+ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)
+ {
+ struct ath11k_base *ab = ar->ab;
+@@ -9295,28 +9860,43 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar)
+ return -ENOMEM;
+ }
+
+- limits[0].max = 1;
+- limits[0].types |= BIT(NL80211_IFTYPE_STATION);
+-
+- limits[1].max = 16;
+- limits[1].types |= BIT(NL80211_IFTYPE_AP);
++ if (ab->hw_params.single_pdev_only && ar->ab->hw_params.num_rxmda_per_pdev > 1) {
++ limits[0].max = 2;
++ limits[0].types |= BIT(NL80211_IFTYPE_STATION);
+
+- if (IS_ENABLED(CONFIG_MAC80211_MESH) &&
+- ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT))
+- limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT);
++ limits[1].max = 1;
++ limits[1].types |= BIT(NL80211_IFTYPE_AP);
+
+- combinations[0].limits = limits;
+- combinations[0].n_limits = n_limits;
+- combinations[0].max_interfaces = 16;
+- combinations[0].num_different_channels = 1;
+- combinations[0].beacon_int_infra_match = true;
+- combinations[0].beacon_int_min_gcd = 100;
+- combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+- BIT(NL80211_CHAN_WIDTH_20) |
+- BIT(NL80211_CHAN_WIDTH_40) |
+- BIT(NL80211_CHAN_WIDTH_80) |
+- BIT(NL80211_CHAN_WIDTH_80P80) |
+- BIT(NL80211_CHAN_WIDTH_160);
++ combinations[0].limits = limits;
++ combinations[0].n_limits = 2;
++ combinations[0].max_interfaces = 3;
++ combinations[0].num_different_channels = 2;
++ combinations[0].beacon_int_infra_match = true;
++ combinations[0].beacon_int_min_gcd = 100;
++ } else {
++ limits[0].max = 1;
++ limits[0].types |= BIT(NL80211_IFTYPE_STATION);
++
++ limits[1].max = 16;
++ limits[1].types |= BIT(NL80211_IFTYPE_AP);
++
++ if (IS_ENABLED(CONFIG_MAC80211_MESH) &&
++ ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT))
++ limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT);
++
++ combinations[0].limits = limits;
++ combinations[0].n_limits = 2;
++ combinations[0].max_interfaces = 16;
++ combinations[0].num_different_channels = 1;
++ combinations[0].beacon_int_infra_match = true;
++ combinations[0].beacon_int_min_gcd = 100;
++ combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
++ BIT(NL80211_CHAN_WIDTH_20) |
++ BIT(NL80211_CHAN_WIDTH_40) |
++ BIT(NL80211_CHAN_WIDTH_80) |
++ BIT(NL80211_CHAN_WIDTH_80P80) |
++ BIT(NL80211_CHAN_WIDTH_160);
++ }
+
+ ar->hw->wiphy->iface_combinations = combinations;
+ ar->hw->wiphy->n_iface_combinations = 1;
+@@ -9367,6 +9947,7 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = {
+
+ static void __ath11k_mac_unregister(struct ath11k *ar)
+ {
++ cancel_work_sync(&ar->channel_update_work);
+ cancel_work_sync(&ar->regd_update_work);
+
+ ieee80211_unregister_hw(ar->hw);
+@@ -9381,6 +9962,8 @@ static void __ath11k_mac_unregister(struct ath11k *ar)
+ kfree(ar->hw->wiphy->iface_combinations[0].limits);
+ kfree(ar->hw->wiphy->iface_combinations);
+
++ kfree(ar->hw->wiphy->addresses);
++
+ SET_IEEE80211_DEV(ar->hw, NULL);
+ }
+
+@@ -9423,6 +10006,7 @@ static int __ath11k_mac_register(struct ath11k *ar)
+ ath11k_pdev_caps_update(ar);
+
+ SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr);
++ ath11k_mac_setup_mac_address_list(ar);
+
+ SET_IEEE80211_DEV(ar->hw, ab->dev);
+
+@@ -9565,6 +10149,8 @@ static int __ath11k_mac_register(struct ath11k *ar)
+ ar->hw->wiphy->iftype_ext_capab = ath11k_iftypes_ext_capa;
+ ar->hw->wiphy->num_iftype_ext_capab =
+ ARRAY_SIZE(ath11k_iftypes_ext_capa);
++ ar->hw->wiphy->vendor_commands = ath11k_vendor_cmds;
++ ar->hw->wiphy->n_vendor_commands = ARRAY_SIZE(ath11k_vendor_cmds);
+
+ if (ar->supports_6ghz) {
+ wiphy_ext_feature_set(ar->hw->wiphy,
+@@ -9766,6 +10352,9 @@ int ath11k_mac_allocate(struct ath11k_base *ab)
+
+ INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work);
+ INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work);
++ INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work);
++ INIT_LIST_HEAD(&ar->channel_update_queue);
++ spin_lock_init(&ar->channel_update_lock);
+
+ INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work);
+ skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
+@@ -9836,3 +10425,27 @@ int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
+
+ return 0;
+ }
++
++static int ath11k_mac_vif_send_coex_config(struct ath11k_vif *arvif,
++ struct wmi_coex_config_params *param)
++{
++ return ath11k_wmi_send_coex_config(arvif->ar, param);
++}
++
++int ath11k_mac_send_coex_config(struct ath11k *ar,
++ struct wmi_coex_config_params *param)
++{
++ struct ath11k_vif *arvif;
++ int ret;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ list_for_each_entry(arvif, &ar->arvifs, list) {
++ param->vdev_id = arvif->vdev_id;
++ ret = ath11k_mac_vif_send_coex_config(arvif, param);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
+diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h
+index 0231783ad..205140e31 100644
+--- a/drivers/net/wireless/ath/ath11k/mac.h
++++ b/drivers/net/wireless/ath/ath11k/mac.h
+@@ -158,7 +158,7 @@ struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab);
+
+ struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id);
+ struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id);
+-
++enum wmi_vdev_type ath11k_mac_get_ar_vdev_type(struct ath11k *ar);
+ void ath11k_mac_drain_tx(struct ath11k *ar);
+ void ath11k_mac_peer_cleanup_all(struct ath11k *ar);
+ int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
+@@ -175,4 +175,9 @@ int ath11k_mac_wait_tx_complete(struct ath11k *ar);
+ int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
+ enum wmi_sta_keepalive_method method,
+ u32 interval);
++int ath11k_mac_send_coex_config(struct ath11k *ar,
++ struct wmi_coex_config_params *param);
++void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
++ struct ieee80211_vif *vif,
++ struct ieee80211_chanctx_conf *ctx);
+ #endif
+diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c
+index 3ac689f1d..24578bd5e 100644
+--- a/drivers/net/wireless/ath/ath11k/mhi.c
++++ b/drivers/net/wireless/ath/ath11k/mhi.c
+@@ -414,7 +414,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci)
+ goto free_controller;
+ } else {
+ mhi_ctrl->iova_start = 0;
+- mhi_ctrl->iova_stop = 0xFFFFFFFF;
++ mhi_ctrl->iova_stop = 0xFFFFFFFFF;
+ }
+
+ mhi_ctrl->rddm_size = RDDM_DUMP_SIZE;
+@@ -434,6 +434,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci)
+ case ATH11K_HW_QCA6390_HW20:
+ case ATH11K_HW_WCN6855_HW20:
+ case ATH11K_HW_WCN6855_HW21:
++ case ATH11K_HW_QCA206X_HW21:
+ ath11k_mhi_config = &ath11k_mhi_config_qca6390;
+ break;
+ default:
+@@ -525,3 +526,18 @@ int ath11k_mhi_resume(struct ath11k_pci *ab_pci)
+
+ return 0;
+ }
++
++
++int ath11k_mhi_force_rddm(struct ath11k_pci *ab_pci)
++{
++ struct ath11k_base *ab = ab_pci->ab;
++ int ret;
++
++ ret = mhi_force_rddm_mode(ab_pci->mhi_ctrl);
++ if (ret) {
++ ath11k_warn(ab, "failed to resume mhi: %d", ret);
++ return ret;
++ }
++
++ return 0;
++}
+diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h
+index 8d9f852da..215428b87 100644
+--- a/drivers/net/wireless/ath/ath11k/mhi.h
++++ b/drivers/net/wireless/ath/ath11k/mhi.h
+@@ -25,5 +25,6 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab);
+
+ int ath11k_mhi_suspend(struct ath11k_pci *ar_pci);
+ int ath11k_mhi_resume(struct ath11k_pci *ar_pci);
++int ath11k_mhi_force_rddm(struct ath11k_pci *ab_pci);
+
+ #endif
+diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
+index 09e65c5e5..59de93c0b 100644
+--- a/drivers/net/wireless/ath/ath11k/pci.c
++++ b/drivers/net/wireless/ath/ath11k/pci.c
+@@ -18,7 +18,7 @@
+ #include "qmi.h"
+
+ #define ATH11K_PCI_BAR_NUM 0
+-#define ATH11K_PCI_DMA_MASK 32
++#define ATH11K_PCI_DMA_MASK 36
+
+ #define TCSR_SOC_HW_VERSION 0x0224
+ #define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(11, 8)
+@@ -28,6 +28,8 @@
+ #define QCN9074_DEVICE_ID 0x1104
+ #define WCN6855_DEVICE_ID 0x1103
+
++#define SUB_VERSION 0x1910010
++
+ static const struct pci_device_id ath11k_pci_id_table[] = {
+ { PCI_VDEVICE(QCOM, QCA6390_DEVICE_ID) },
+ { PCI_VDEVICE(QCOM, WCN6855_DEVICE_ID) },
+@@ -37,6 +39,124 @@ static const struct pci_device_id ath11k_pci_id_table[] = {
+
+ MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table);
+
++static struct cnss_pci_reg register_rddm_fail_debug[] = {
++ {"PCIE_BHI_VERSION_LOWER", HWIO_PCIE_PCIE_BHI_VERSION_LOWER_ADDR, 0},
++ {"PCIE_BHI_VERSION_UPPER", HWIO_PCIE_PCIE_BHI_VERSION_UPPER_ADDR, 0},
++ {"PCIE_BHI_IMGADDR_LOWER", HWIO_PCIE_PCIE_BHI_IMGADDR_LOWER_ADDR, 0},
++ {"PCIE_BHI_IMGADDR_UPPER", HWIO_PCIE_PCIE_BHI_IMGADDR_UPPER_ADDR, 0},
++ {"PCIE_BHI_IMGSIZE", HWIO_PCIE_PCIE_BHI_IMGSIZE_ADDR, 0},
++ {"PCIE_BHI_IMGTXDB", HWIO_PCIE_PCIE_BHI_IMGTXDB_ADDR, 0},
++ {"PCIE_BHI_INTVEC", HWIO_PCIE_PCIE_BHI_INTVEC_ADDR, 0},
++ {"PCIE_BHI_EXECENV", HWIO_PCIE_PCIE_BHI_EXECENV_ADDR, 0},
++ {"PCIE_BHI_STATUS", HWIO_PCIE_PCIE_BHI_STATUS_ADDR, 0},
++ {"PCIE_BHI_ERRCODE", HWIO_PCIE_PCIE_BHI_ERRCODE_ADDR, 0},
++ {"PCIE_BHI_ERRDBG1", HWIO_PCIE_PCIE_BHI_ERRDBG1_ADDR, 0},
++ {"PCIE_BHI_ERRDBG2", HWIO_PCIE_PCIE_BHI_ERRDBG2_ADDR, 0},
++ {"PCIE_BHI_ERRDBG3", HWIO_PCIE_PCIE_BHI_ERRDBG3_ADDR, 0},
++ {"PCIE_BHI_SERIALNUM", HWIO_PCIE_PCIE_BHI_SERIALNUM_ADDR, 0},
++ {"PCIE_BHI_SBLANTIROLLVER", HWIO_PCIE_PCIE_BHI_SBLANTIROLLVER_ADDR, 0},
++ {"PCIE_BHI_NUMSEG", HWIO_PCIE_PCIE_BHI_NUMSEG_ADDR, 0},
++ {"PCIE_BHI_MSMHWID_0", HWIO_PCIE_PCIE_BHI_MSMHWID_0_ADDR, 0},
++ {"PCIE_BHI_MSMHWID_1", HWIO_PCIE_PCIE_BHI_MSMHWID_1_ADDR, 0},
++ {"PCIE_BHI_MSMHWID_2", HWIO_PCIE_PCIE_BHI_MSMHWID_2_ADDR, 0},
++ {"PCIE_BHI_MSMHWID_3", HWIO_PCIE_PCIE_BHI_MSMHWID_3_ADDR, 0},
++ {"PCIE_BHI_MSMHWID_4", HWIO_PCIE_PCIE_BHI_MSMHWID_4_ADDR, 0},
++ {"PCIE_BHI_MSMHWID_5", HWIO_PCIE_PCIE_BHI_MSMHWID_5_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_0", HWIO_PCIE_PCIE_BHI_OEMPKHASH_0_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_1", HWIO_PCIE_PCIE_BHI_OEMPKHASH_1_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_2", HWIO_PCIE_PCIE_BHI_OEMPKHASH_2_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_3", HWIO_PCIE_PCIE_BHI_OEMPKHASH_3_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_4", HWIO_PCIE_PCIE_BHI_OEMPKHASH_4_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_5", HWIO_PCIE_PCIE_BHI_OEMPKHASH_5_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_6", HWIO_PCIE_PCIE_BHI_OEMPKHASH_6_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_7", HWIO_PCIE_PCIE_BHI_OEMPKHASH_7_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_8", HWIO_PCIE_PCIE_BHI_OEMPKHASH_8_ADDR, 0},
++ {"PCIE_BHI_OEMPKHASH_9", HWIO_PCIE_PCIE_BHI_OEMPKHASH_9_ADDR, 0},
++ {"PCIE_BHI_TXVECDB", HWIO_PCIE_PCIE_BHI_TXVECDB_ADDR, 0},
++ {"PCIE_BHI_TXVECSTATUS", HWIO_PCIE_PCIE_BHI_TXVECSTATUS_ADDR, 0},
++ {"PCIE_BHI_RXVECDB", HWIO_PCIE_PCIE_BHI_RXVECDB_ADDR, 0},
++ {"PCIE_BHI_RXVECSTATUS", HWIO_PCIE_PCIE_BHI_RXVECSTATUS_ADDR, 0},
++ /* After dump this register, recovery will fail for QCA6490 */
++ //{"PCIE_WLAON_RESET_DBG_SW_ENTRY", WLAON_RESET_DBG_SW_ENTRY, 0},
++ {NULL},
++};
++
++static struct cnss_pci_reg register_to_dump[] = {
++ {"QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL", QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL, 0},
++ {"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF, 0},
++ {"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0, 0},
++ {"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1, 0},
++ {"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0, 0},
++ {"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1, 0},
++ {"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2, 0},
++ {NULL},
++};
++
++#define LINE_LEN_MAX 80
++
++static int ath11k_register_dump(struct ath11k_base *ab,
++ u8 **buf,
++ size_t *buf_len,
++ struct cnss_pci_reg *regs,
++ size_t num)
++{
++ u32 i;
++ u32 offset = 0;
++ size_t line_len;
++ ssize_t len;
++ char (*line_ptr)[LINE_LEN_MAX];
++
++ *buf_len = 0;
++
++ line_ptr = (char (*)[LINE_LEN_MAX])vzalloc(num * LINE_LEN_MAX);
++ if (!line_ptr)
++ return -ENOMEM;
++
++ for (i = 0; regs[i].name; i++) {
++ regs[i].value = ath11k_pcic_read32(ab, regs[i].offset);
++ ath11k_info(ab, "%s[0x%x] = 0x%x\n",
++ regs[i].name, regs[i].offset, regs[i].value);
++ len = snprintf(line_ptr[i], LINE_LEN_MAX, "%s[0x%x] = 0x%x\n",
++ regs[i].name, regs[i].offset, regs[i].value);
++ *buf_len += len;
++ }
++
++ ath11k_info(ab, "%s buf len=%lu\n", __func__, *buf_len);
++ *buf = vzalloc(*buf_len);
++ if (!*buf)
++ return -ENOMEM;
++
++ for (i = 0; i < num; ++i) {
++ line_len = strlen(line_ptr[i]);
++ memcpy(*buf + offset, line_ptr[i], line_len);
++ offset += line_len;
++ }
++
++ vfree(line_ptr);
++
++ return 0;
++}
++
++void ath11k_pci_register_dump(struct ath11k_pci *ab_pci)
++{
++ size_t num;
++ struct register_crash_data *crash_data = &ab_pci->reg_data;
++
++ num = sizeof(register_to_dump) / sizeof(struct cnss_pci_reg) - 1;
++ ath11k_register_dump(ab_pci->ab,
++ &crash_data->reg_buf,
++ &crash_data->reg_buf_len,
++ register_to_dump,
++ num);
++
++ num = sizeof(register_rddm_fail_debug) / sizeof(struct cnss_pci_reg) - 1;
++ ath11k_register_dump(ab_pci->ab,
++ &crash_data->reg_rddm_buf,
++ &crash_data->reg_rddm_buf_len,
++ register_rddm_fail_debug,
++ num);
++}
++
+ static int ath11k_pci_bus_wake_up(struct ath11k_base *ab)
+ {
+ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+@@ -108,7 +228,12 @@ static u32 ath11k_pci_window_read32(struct ath11k_base *ab, u32 offset)
+ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+ u32 window_start, val;
+
+- window_start = ath11k_pci_get_window_start(ab, offset);
++ if (ab->hw_params.static_window_map)
++ window_start = ath11k_pci_get_window_start(ab, offset);
++ else
++ window_start = ATH11K_PCI_WINDOW_START;
++
++ //window_start = ath11k_pci_get_window_start(ab, offset);
+
+ if (window_start == ATH11K_PCI_WINDOW_START) {
+ spin_lock_bh(&ab_pci->window_lock);
+@@ -669,19 +794,21 @@ static int ath11k_pci_start(struct ath11k_base *ab)
+ {
+ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+
+- /* TODO: for now don't restore ASPM in case of single MSI
+- * vector as MHI register reading in M2 causes system hang.
+- */
+- if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
+- ath11k_pci_aspm_restore(ab_pci);
+- else
+- ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n");
++ ath11k_pci_aspm_restore(ab_pci);
+
+ ath11k_pcic_start(ab);
+
+ return 0;
+ }
+
++static int ath11k_pci_force_rddm(struct ath11k_base *ab)
++{
++ struct ath11k_pci *ar_pci;
++
++ ar_pci = ath11k_pci_priv(ab);
++ return ath11k_mhi_force_rddm(ar_pci);
++}
++
+ static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
+ .start = ath11k_pci_start,
+ .stop = ath11k_pcic_stop,
+@@ -700,6 +827,7 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
+ .ce_irq_enable = ath11k_pci_hif_ce_irq_enable,
+ .ce_irq_disable = ath11k_pci_hif_ce_irq_disable,
+ .get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx,
++ .target_crash = ath11k_pci_force_rddm,
+ };
+
+ static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor)
+@@ -733,6 +861,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
+ u32 soc_hw_version_major, soc_hw_version_minor, addr;
+ const struct ath11k_pci_ops *pci_ops;
+ int ret;
++ u32 sub_version;
++ int ops_init = 0;
+
+ ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI);
+
+@@ -800,6 +930,13 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
+ ab->id.bdf_search = ATH11K_BDF_SEARCH_BUS_AND_BOARD;
+ ath11k_pci_read_hw_version(ab, &soc_hw_version_major,
+ &soc_hw_version_minor);
++ pci_ops = &ath11k_pci_ops_qca6390;
++ ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
++ if (ret) {
++ ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
++ goto err_pci_free_region;
++ }
++ ops_init = 1;
+ switch (soc_hw_version_major) {
+ case 2:
+ switch (soc_hw_version_minor) {
+@@ -809,7 +946,19 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
+ break;
+ case 0x10:
+ case 0x11:
+- ab->hw_rev = ATH11K_HW_WCN6855_HW21;
++ //ab->hw_rev = ATH11K_HW_WCN6855_HW21;
++ sub_version = ath11k_pcic_read32(ab, SUB_VERSION);
++ ath11k_dbg(ab, ATH11K_DBG_PCI, "sub_version 0x%x\n", sub_version);
++ switch (sub_version) {
++ case 0x1019A0E1:
++ case 0x1019B0E1:
++ case 0x1019C0E1:
++ case 0x1019D0E1:
++ ab->hw_rev = ATH11K_HW_QCA206X_HW21;
++ break;
++ default:
++ ab->hw_rev = ATH11K_HW_WCN6855_HW21;
++ }
+ break;
+ default:
+ goto unsupported_wcn6855_soc;
+@@ -823,7 +972,6 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
+ goto err_pci_free_region;
+ }
+
+- pci_ops = &ath11k_pci_ops_qca6390;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n",
+@@ -832,10 +980,12 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
+ goto err_pci_free_region;
+ }
+
+- ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
+- if (ret) {
+- ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
+- goto err_pci_free_region;
++ if(ops_init == 1){
++ ret = ath11k_pcic_register_pci_ops(ab, pci_ops);
++ if (ret) {
++ ath11k_err(ab, "failed to register PCI ops: %d\n", ret);
++ goto err_pci_free_region;
++ }
+ }
+
+ ret = ath11k_pcic_init_msi_config(ab);
+@@ -1021,6 +1171,7 @@ static struct pci_driver ath11k_pci_driver = {
+ static int ath11k_pci_init(void)
+ {
+ int ret;
++ u32 sub_version;
+
+ ret = pci_register_driver(&ath11k_pci_driver);
+ if (ret)
+diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h
+index e9a01f344..2b13b2900 100644
+--- a/drivers/net/wireless/ath/ath11k/pci.h
++++ b/drivers/net/wireless/ath/ath11k/pci.h
+@@ -14,6 +14,7 @@
+ #define PCIE_SOC_GLOBAL_RESET_V 1
+
+ #define WLAON_WARM_SW_ENTRY 0x1f80504
++#define WLAON_RESET_DBG_SW_ENTRY 0x01F80508
+ #define WLAON_SOC_RESET_CAUSE_REG 0x01f8060c
+
+ #define PCIE_Q6_COOKIE_ADDR 0x01f80500
+@@ -53,6 +54,65 @@
+ #define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c
+ #define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4
+
++#define HWIO_PCIE_PCIE_BHI_VERSION_LOWER_ADDR (0x1e0e200)
++#define HWIO_PCIE_PCIE_BHI_VERSION_UPPER_ADDR (0x1e0e204)
++#define HWIO_PCIE_PCIE_BHI_IMGADDR_LOWER_ADDR (0x1e0e208)
++#define HWIO_PCIE_PCIE_BHI_IMGADDR_UPPER_ADDR (0x1e0e20c)
++#define HWIO_PCIE_PCIE_BHI_IMGSIZE_ADDR (0x1e0e210)
++#define HWIO_PCIE_PCIE_BHI_IMGTXDB_ADDR (0x1e0e218)
++#define HWIO_PCIE_PCIE_BHI_INTVEC_ADDR (0x1e0e220)
++#define HWIO_PCIE_PCIE_BHI_EXECENV_ADDR (0x1e0e228)
++#define HWIO_PCIE_PCIE_BHI_STATUS_ADDR (0x1e0e22c)
++#define HWIO_PCIE_PCIE_BHI_ERRCODE_ADDR (0x1e0e230)
++#define HWIO_PCIE_PCIE_BHI_ERRDBG1_ADDR (0x1e0e234)
++#define HWIO_PCIE_PCIE_BHI_ERRDBG2_ADDR (0x1e0e238)
++#define HWIO_PCIE_PCIE_BHI_ERRDBG3_ADDR (0x1e0e23c)
++#define HWIO_PCIE_PCIE_BHI_SERIALNUM_ADDR (0x1e0e240)
++#define HWIO_PCIE_PCIE_BHI_SBLANTIROLLVER_ADDR (0x1e0e244)
++#define HWIO_PCIE_PCIE_BHI_NUMSEG_ADDR (0x1e0e248)
++#define HWIO_PCIE_PCIE_BHI_MSMHWID_0_ADDR (0x1e0e24c)
++#define HWIO_PCIE_PCIE_BHI_MSMHWID_1_ADDR (0x1e0e250)
++#define HWIO_PCIE_PCIE_BHI_MSMHWID_2_ADDR (0x1e0e254)
++#define HWIO_PCIE_PCIE_BHI_MSMHWID_3_ADDR (0x1e0e258)
++#define HWIO_PCIE_PCIE_BHI_MSMHWID_4_ADDR (0x1e0e25c)
++#define HWIO_PCIE_PCIE_BHI_MSMHWID_5_ADDR (0x1e0e260)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_0_ADDR (0x1e0e264)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_1_ADDR (0x1e0e268)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_2_ADDR (0x1e0e26c)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_3_ADDR (0x1e0e270)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_4_ADDR (0x1e0e274)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_5_ADDR (0x1e0e278)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_6_ADDR (0x1e0e27c)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_7_ADDR (0x1e0e280)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_8_ADDR (0x1e0e284)
++#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_9_ADDR (0x1e0e288)
++#define HWIO_PCIE_PCIE_BHI_TXVECDB_ADDR (0x1e0e360)
++#define HWIO_PCIE_PCIE_BHI_TXVECSTATUS_ADDR (0x1e0e368)
++#define HWIO_PCIE_PCIE_BHI_RXVECDB_ADDR (0x1e0e394)
++#define HWIO_PCIE_PCIE_BHI_RXVECSTATUS_ADDR (0x1e0e39c)
++
++#define QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL (0x1C0106C)
++#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF (0x1C01070)
++#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0 (0x1C01074)
++#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1 (0x1C01078)
++#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0 (0x00DA102C)
++#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1 (0x00DA202C)
++#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2 (0x00DA302C)
++
++struct cnss_pci_reg {
++ char *name;
++ u32 offset;
++ u32 value;
++};
++
++struct register_crash_data {
++ u8 *reg_buf;
++ size_t reg_buf_len;
++ u8 *reg_rddm_buf;
++ size_t reg_rddm_buf_len;
++};
++
++
+ enum ath11k_pci_flags {
+ ATH11K_PCI_ASPM_RESTORE,
+ };
+@@ -72,6 +132,8 @@ struct ath11k_pci {
+ /* enum ath11k_pci_flags */
+ unsigned long flags;
+ u16 link_ctl;
++ struct register_crash_data reg_data;
++
+ };
+
+ static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
+@@ -80,4 +142,5 @@ static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
+ }
+
+ int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector);
++void ath11k_pci_register_dump(struct ath11k_pci *ab_pci);
+ #endif
+diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c
+index c63083633..2f95d5b68 100644
+--- a/drivers/net/wireless/ath/ath11k/pcic.c
++++ b/drivers/net/wireless/ath/ath11k/pcic.c
+@@ -115,6 +115,17 @@ static const struct ath11k_msi_config ath11k_msi_config[] = {
+ },
+ .hw_rev = ATH11K_HW_WCN6750_HW10,
+ },
++ {
++ .total_vectors = 32,
++ .total_users = 4,
++ .users = (struct ath11k_msi_user[]) {
++ { .name = "MHI", .num_vectors = 3, .base_vector = 0 },
++ { .name = "CE", .num_vectors = 10, .base_vector = 3 },
++ { .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
++ { .name = "DP", .num_vectors = 18, .base_vector = 14 },
++ },
++ .hw_rev = ATH11K_HW_QCA206X_HW21,
++ },
+ };
+
+ int ath11k_pcic_init_msi_config(struct ath11k_base *ab)
+@@ -460,8 +471,6 @@ void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)
+ {
+ int i;
+
+- set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
+-
+ for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
+ struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
+
+@@ -471,6 +480,8 @@ void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)
+ }
+ ath11k_pcic_ext_grp_enable(irq_grp);
+ }
++
++ set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
+ }
+ EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable);
+
+@@ -598,7 +609,10 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
+ ath11k_dbg(ab, ATH11K_DBG_PCI,
+ "irq %d group %d\n", irq, i);
+
+- irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
++ irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY | IRQ_MOVE_PCNTXT);
++ else
++ irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
+ ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler,
+ irq_flags, "DP_EXT_IRQ", irq_grp);
+ if (ret) {
+@@ -648,6 +662,8 @@ int ath11k_pcic_config_irq(struct ath11k_base *ab)
+
+ tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet);
+
++ if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
++ irq_set_status_flags(irq, IRQ_MOVE_PCNTXT);
+ ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler,
+ irq_flags, irq_name[irq_idx], ce_pipe);
+ if (ret) {
+diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
+index 41fad03a3..a5f582638 100644
+--- a/drivers/net/wireless/ath/ath11k/qmi.c
++++ b/drivers/net/wireless/ath/ath11k/qmi.c
+@@ -1971,6 +1971,51 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab)
+ }
+ }
+
++static size_t ath11k_qmi_get_remote_buf_len(struct fw_remote_mem *fw_mem)
++{
++ unsigned int i;
++ size_t len = 0;
++
++ for (i = 0; i < ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01; i++) {
++ if (fw_mem[i].vaddr && fw_mem[i].size)
++ len += fw_mem[i].size;
++ }
++ return len;
++}
++
++int ath11k_qmi_remote_dump(struct ath11k_base *ab)
++{
++ struct fw_remote_crash_data *crash_data = &ab->remote_crash_data;
++ struct fw_remote_mem *fw_mem = ab->remote_mem;
++ u8 i;
++ u32 offset = 0;
++
++ crash_data->remote_buf_len = ath11k_qmi_get_remote_buf_len(fw_mem);
++ ath11k_info(ab, "%s remote buffer len=%lu\n", __func__, crash_data->remote_buf_len);
++ crash_data->remote_buf = vzalloc(crash_data->remote_buf_len);
++ if (!crash_data->remote_buf)
++ return -ENOMEM;
++
++ for (i = 0; i < ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01; i++) {
++ if (fw_mem[i].vaddr && fw_mem[i].size) {
++ ath11k_info(ab, "remote mem: 0x%p, size: 0x%lx\n", fw_mem[i].vaddr,
++ fw_mem[i].size);
++ memcpy(crash_data->remote_buf + offset, fw_mem[i].vaddr, fw_mem[i].size);
++ offset += fw_mem[i].size;
++ }
++ }
++ return 0;
++}
++EXPORT_SYMBOL(ath11k_qmi_remote_dump);
++
++static void ath11k_qmi_set_remote_mem(struct fw_remote_mem *fw_mem,
++ void *vaddr, size_t size,
++ uint32_t segnum)
++{
++ fw_mem[segnum].vaddr = vaddr;
++ fw_mem[segnum].size = size;
++}
++
+ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
+ {
+ int i;
+@@ -1995,10 +2040,10 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
+ chunk->vaddr = NULL;
+ }
+
+- chunk->vaddr = dma_alloc_coherent(ab->dev,
++ chunk->vaddr = ath11k_core_dma_alloc_coherent(ab->dev,
+ chunk->size,
+ &chunk->paddr,
+- GFP_KERNEL | __GFP_NOWARN);
++ GFP_KERNEL | __GFP_NOWARN | GFP_DMA32);
+ if (!chunk->vaddr) {
+ if (ab->qmi.mem_seg_count <= ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT) {
+ ath11k_dbg(ab, ATH11K_DBG_QMI,
+@@ -2015,6 +2060,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
+ chunk->type);
+ return -EINVAL;
+ }
++
++ if (chunk->type == QMI_WLANFW_MEM_TYPE_DDR_V01) {
++ ath11k_qmi_set_remote_mem(ab->remote_mem,
++ chunk->vaddr,
++ chunk->size,
++ i);
++ ath11k_info(ab, "vaddr=0x%p size=0x%x\n", chunk->vaddr, chunk->size);
++ }
++
+ chunk->prev_type = chunk->type;
+ chunk->prev_size = chunk->size;
+ }
+@@ -2518,9 +2572,9 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab)
+ if (m3_mem->vaddr || m3_mem->size)
+ goto skip_m3_alloc;
+
+- m3_mem->vaddr = dma_alloc_coherent(ab->dev,
++ m3_mem->vaddr = ath11k_core_dma_alloc_coherent(ab->dev,
+ fw->size, &m3_mem->paddr,
+- GFP_KERNEL);
++ GFP_KERNEL | GFP_DMA32);
+ if (!m3_mem->vaddr) {
+ ath11k_err(ab, "failed to allocate memory for M3 with size %zu\n",
+ fw->size);
+@@ -3306,6 +3360,9 @@ int ath11k_qmi_init_service(struct ath11k_base *ab)
+ spin_lock_init(&ab->qmi.event_lock);
+ INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work);
+
++ memset(ab->remote_mem, 0,
++ sizeof(struct fw_remote_mem) * ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01);
++
+ ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01,
+ ATH11K_QMI_WLFW_SERVICE_VERS_V01,
+ ab->qmi.service_ins_id);
+diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h
+index d477e2be8..a5c8887b7 100644
+--- a/drivers/net/wireless/ath/ath11k/qmi.h
++++ b/drivers/net/wireless/ath/ath11k/qmi.h
+@@ -518,5 +518,6 @@ void ath11k_qmi_deinit_service(struct ath11k_base *ab);
+ int ath11k_qmi_init_service(struct ath11k_base *ab);
+ void ath11k_qmi_free_resource(struct ath11k_base *ab);
+ int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab);
++int ath11k_qmi_remote_dump(struct ath11k_base *ab);
+
+ #endif
+diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
+index 7f9fb968d..9f28c82c2 100644
+--- a/drivers/net/wireless/ath/ath11k/reg.c
++++ b/drivers/net/wireless/ath/ath11k/reg.c
+@@ -55,6 +55,17 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
+ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+ "Regulatory Notification received for %s\n", wiphy_name(wiphy));
+
++ if ((request->initiator == NL80211_REGDOM_SET_BY_DRIVER) &&
++ (ar->state == ATH11K_STATE_ON)) {
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "dynamically updated by driver\n");
++ ret = ath11k_reg_update_chan_list(ar, true);
++ if (ret)
++ ath11k_warn(ar->ab, "failed to update channel list: %d\n", ret);
++
++ return;
++ }
++
+ /* Currently supporting only General User Hints. Cell base user
+ * hints to be handled later.
+ * Hints from other sources like Core, Beacons are not expected for
+@@ -112,32 +123,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait)
+ struct channel_param *ch;
+ enum nl80211_band band;
+ int num_channels = 0;
+- int i, ret, left;
+-
+- if (wait && ar->state_11d != ATH11K_11D_IDLE) {
+- left = wait_for_completion_timeout(&ar->completed_11d_scan,
+- ATH11K_SCAN_TIMEOUT_HZ);
+- if (!left) {
+- ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+- "failed to receive 11d scan complete: timed out\n");
+- ar->state_11d = ATH11K_11D_IDLE;
+- }
+- ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+- "11d scan wait left time %d\n", left);
+- }
+-
+- if (wait &&
+- (ar->scan.state == ATH11K_SCAN_STARTING ||
+- ar->scan.state == ATH11K_SCAN_RUNNING)) {
+- left = wait_for_completion_timeout(&ar->scan.completed,
+- ATH11K_SCAN_TIMEOUT_HZ);
+- if (!left)
+- ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+- "failed to receive hw scan complete: timed out\n");
+-
+- ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+- "hw scan wait left time %d\n", left);
+- }
++ int i, ret = 0;
+
+ if (ar->state == ATH11K_STATE_RESTARTING)
+ return 0;
+@@ -219,8 +205,15 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait)
+ }
+ }
+
+- ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params);
+- kfree(params);
++ if (wait) {
++ spin_lock_bh(&ar->channel_update_lock);
++ list_add_tail(&params->list, &ar->channel_update_queue);
++ spin_unlock_bh(&ar->channel_update_lock);
++ queue_work(ar->ab->workqueue, &ar->channel_update_work);
++ } else {
++ ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params);
++ kfree(params);
++ }
+
+ return ret;
+ }
+@@ -294,12 +287,6 @@ int ath11k_regd_update(struct ath11k *ar)
+ if (ret)
+ goto err;
+
+- if (ar->state == ATH11K_STATE_ON) {
+- ret = ath11k_reg_update_chan_list(ar, true);
+- if (ret)
+- goto err;
+- }
+-
+ return 0;
+ err:
+ ath11k_warn(ab, "failed to perform regd update : %d\n", ret);
+@@ -413,6 +400,10 @@ static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1,
+
+ /* Use the flags of both the rules */
+ new_rule->flags = rule1->flags | rule2->flags;
++ if ((rule1->flags & NL80211_RRF_PSD) && (rule2->flags & NL80211_RRF_PSD))
++ new_rule->psd = min_t(s8, rule1->psd, rule2->psd);
++ else
++ new_rule->flags &= ~NL80211_RRF_PSD;
+
+ /* To be safe, lts use the max cac timeout of both rules */
+ new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms,
+@@ -516,13 +507,14 @@ ath11k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw)
+ static void
+ ath11k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq,
+ u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr,
+- u32 reg_flags)
++ s8 psd, u32 reg_flags)
+ {
+ reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq);
+ reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq);
+ reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw);
+ reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain);
+ reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr);
++ reg_rule->psd = psd;
+ reg_rule->flags = reg_flags;
+ }
+
+@@ -552,7 +544,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
+ reg_rule->start_freq,
+ ETSI_WEATHER_RADAR_BAND_LOW, bw,
+ reg_rule->ant_gain, reg_rule->reg_power,
+- flags);
++ reg_rule->psd_eirp, flags);
+
+ ath11k_dbg(ab, ATH11K_DBG_REG,
+ "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
+@@ -573,7 +565,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
+
+ ath11k_reg_update_rule(regd->reg_rules + i, start_freq,
+ end_freq, bw, reg_rule->ant_gain,
+- reg_rule->reg_power, flags);
++ reg_rule->reg_power, reg_rule->psd_eirp, flags);
+
+ regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT;
+
+@@ -594,7 +586,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
+ ETSI_WEATHER_RADAR_BAND_HIGH,
+ reg_rule->end_freq, bw,
+ reg_rule->ant_gain, reg_rule->reg_power,
+- flags);
++ reg_rule->psd_eirp, flags);
+
+ ath11k_dbg(ab, ATH11K_DBG_REG,
+ "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
+@@ -607,25 +599,64 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
+ *rule_idx = i;
+ }
+
++enum wmi_reg_6ghz_ap_type
++ath11k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type)
++{
++ switch (power_type) {
++ case IEEE80211_REG_LPI_AP:
++ return WMI_REG_INDOOR_AP;
++ case IEEE80211_REG_SP_AP:
++ return WMI_REG_STANDARD_POWER_AP;
++ case IEEE80211_REG_VLP_AP:
++ return WMI_REG_VERY_LOW_POWER_AP;
++ default:
++ return WMI_REG_MAX_AP_TYPE;
++ }
++}
++
+ struct ieee80211_regdomain *
+ ath11k_reg_build_regd(struct ath11k_base *ab,
+- struct cur_regulatory_info *reg_info, bool intersect)
++ struct cur_regulatory_info *reg_info, bool intersect,
++ enum wmi_vdev_type vdev_type,
++ enum ieee80211_ap_reg_power power_type)
+ {
+ struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
+- struct cur_reg_rule *reg_rule;
++ struct cur_reg_rule *reg_rule, *reg_rule_6ghz;
+ u8 i = 0, j = 0, k = 0;
+ u8 num_rules;
+ u16 max_bw;
+- u32 flags;
++ u32 flags, reg_6ghz_number, max_bw_6ghz;
+ char alpha2[3];
+
+ num_rules = reg_info->num_5ghz_reg_rules + reg_info->num_2ghz_reg_rules;
+
+- /* FIXME: Currently taking reg rules for 6 GHz only from Indoor AP mode list.
+- * This can be updated after complete 6 GHz regulatory support is added.
+- */
+- if (reg_info->is_ext_reg_event)
+- num_rules += reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP];
++ if (reg_info->is_ext_reg_event) {
++ if (vdev_type == WMI_VDEV_TYPE_STA) {
++ enum wmi_reg_6ghz_ap_type ap_type;
++
++ ap_type = ath11k_ieee80211_ap_pwr_type_convert(power_type);
++
++ if (ap_type == WMI_REG_MAX_AP_TYPE)
++ ap_type = WMI_REG_INDOOR_AP;
++ reg_6ghz_number = reg_info->num_6ghz_rules_client
++ [ap_type][WMI_REG_DEFAULT_CLIENT];
++ if (reg_6ghz_number == 0) {
++ ap_type = WMI_REG_INDOOR_AP;
++ reg_6ghz_number = reg_info->num_6ghz_rules_client
++ [ap_type][WMI_REG_DEFAULT_CLIENT];
++ }
++ reg_rule_6ghz = reg_info->reg_rules_6ghz_client_ptr
++ [ap_type][WMI_REG_DEFAULT_CLIENT];
++ max_bw_6ghz = reg_info->max_bw_6ghz_client
++ [ap_type][WMI_REG_DEFAULT_CLIENT];
++ } else {
++ reg_6ghz_number = reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP];
++ reg_rule_6ghz =
++ reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP];
++ max_bw_6ghz = reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP];
++ }
++ num_rules += reg_6ghz_number;
++ }
+
+ if (!num_rules)
+ goto ret;
+@@ -672,14 +703,13 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
+ * per other BW rule flags we pass from here
+ */
+ flags = NL80211_RRF_AUTO_BW;
+- } else if (reg_info->is_ext_reg_event &&
+- reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP] &&
+- (k < reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP])) {
+- reg_rule = reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP] +
+- k++;
+- max_bw = min_t(u16, reg_rule->max_bw,
+- reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP]);
++ } else if (reg_info->is_ext_reg_event && reg_6ghz_number &&
++ (k < reg_6ghz_number)) {
++ reg_rule = reg_rule_6ghz + k++;
++ max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz);
+ flags = NL80211_RRF_AUTO_BW;
++ if (reg_rule->psd_flag)
++ flags |= NL80211_RRF_PSD;
+ } else {
+ break;
+ }
+@@ -690,7 +720,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
+ reg_rule->start_freq,
+ reg_rule->end_freq, max_bw,
+ reg_rule->ant_gain, reg_rule->reg_power,
+- flags);
++ reg_rule->psd_eirp, flags);
+
+ /* Update dfs cac timeout if the dfs domain is ETSI and the
+ * new rule covers weather radar band.
+@@ -746,6 +776,50 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
+ return new_regd;
+ }
+
++void ath11k_regd_update_chan_list_work(struct work_struct *work)
++{
++ struct ath11k *ar = container_of(work, struct ath11k,
++ channel_update_work);
++ struct scan_chan_list_params *params, *tmp;
++ int left;
++
++ spin_lock_bh(&ar->channel_update_lock);
++
++ list_for_each_entry_safe(params, tmp, &ar->channel_update_queue, list) {
++ list_del(&params->list);
++ spin_unlock_bh(&ar->channel_update_lock);
++
++ if (ar->state_11d != ATH11K_11D_IDLE) {
++ left = wait_for_completion_timeout(&ar->completed_11d_scan,
++ ATH11K_SCAN_TIMEOUT_HZ);
++ if (!left) {
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "failed to receive 11d scan complete: timed out\n");
++ ar->state_11d = ATH11K_11D_IDLE;
++ }
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "reg 11d scan wait left time %d\n", left);
++ }
++
++ if ((ar->scan.state == ATH11K_SCAN_STARTING ||
++ ar->scan.state == ATH11K_SCAN_RUNNING)) {
++ left = wait_for_completion_timeout(&ar->scan.completed,
++ ATH11K_SCAN_TIMEOUT_HZ);
++ if (!left)
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "failed to receive hw scan complete: timed out\n");
++
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "reg hw scan wait left time %d\n", left);
++ }
++
++ ath11k_wmi_send_scan_chan_list_cmd(ar, params);
++ kfree(params);
++ spin_lock_bh(&ar->channel_update_lock);
++ }
++ spin_unlock_bh(&ar->channel_update_lock);
++}
++
+ void ath11k_regd_update_work(struct work_struct *work)
+ {
+ struct ath11k *ar = container_of(work, struct ath11k,
+@@ -766,6 +840,7 @@ void ath11k_regd_update_work(struct work_struct *work)
+ void ath11k_reg_init(struct ath11k *ar)
+ {
+ ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
++ ar->hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER;
+ ar->hw->wiphy->reg_notifier = ath11k_reg_notifier;
+ }
+
+@@ -773,6 +848,12 @@ void ath11k_reg_free(struct ath11k_base *ab)
+ {
+ int i;
+
++ for (i = 0; i < ab->num_radios; i++)
++ ath11k_reg_reset_info(&ab->reg_info_store[i]);
++
++ kfree(ab->reg_info_store);
++ ab->reg_info_store = NULL;
++
+ for (i = 0; i < ab->hw_params.max_radios; i++) {
+ kfree(ab->default_regd[i]);
+ kfree(ab->new_regd[i]);
+diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h
+index 2f284f263..fef5927c6 100644
+--- a/drivers/net/wireless/ath/ath11k/reg.h
++++ b/drivers/net/wireless/ath/ath11k/reg.h
+@@ -28,9 +28,14 @@ enum ath11k_dfs_region {
+ void ath11k_reg_init(struct ath11k *ar);
+ void ath11k_reg_free(struct ath11k_base *ab);
+ void ath11k_regd_update_work(struct work_struct *work);
++void ath11k_regd_update_chan_list_work(struct work_struct *work);
+ struct ieee80211_regdomain *
+ ath11k_reg_build_regd(struct ath11k_base *ab,
+- struct cur_regulatory_info *reg_info, bool intersect);
++ struct cur_regulatory_info *reg_info, bool intersect,
++ enum wmi_vdev_type vdev_type,
++ enum ieee80211_ap_reg_power power_type);
++enum wmi_reg_6ghz_ap_type
++ath11k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type);
+ int ath11k_regd_update(struct ath11k *ar);
+ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait);
+ #endif
+diff --git a/drivers/net/wireless/ath/ath11k/testmode.h b/drivers/net/wireless/ath/ath11k/testmode.h
+index 2f62f2c44..a944086cf 100644
+--- a/drivers/net/wireless/ath/ath11k/testmode.h
++++ b/drivers/net/wireless/ath/ath11k/testmode.h
+@@ -8,17 +8,29 @@
+
+ #ifdef CONFIG_NL80211_TESTMODE
+
+-void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id, struct sk_buff *skb);
++void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
++ struct sk_buff *skb);
++int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
++ const struct wmi_ftm_event_msg *ftm_msg,
++ u16 length);
+ int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len);
+
+ #else
+
+-static inline void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id,
+- struct sk_buff *skb)
++static inline void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab,
++ u32 cmd_id,
++ struct sk_buff *skb)
+ {
+ }
+
++static inline int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
++ const struct wmi_ftm_event_msg *msg,
++ u16 length)
++{
++ return 0;
++}
++
+ static inline int ath11k_tm_cmd(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void *data, int len)
+diff --git a/drivers/net/wireless/ath/ath11k/unitest.c b/drivers/net/wireless/ath/ath11k/unitest.c
+new file mode 100644
+index 000000000..541925af0
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath11k/unitest.c
+@@ -0,0 +1,96 @@
++// SPDX-License-Identifier: BSD-3-Clause-Clear
++/**
++ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
++ */
++
++#include <net/netlink.h>
++#include "debug.h"
++#include "unitest.h"
++
++const struct nla_policy ath11k_unitest_policy[UNITEST_MAX + 1] = {
++ [UNITEST_MODULE_ID] = {.type = NLA_U32},
++ [UNITEST_ARGS_NUM] = {.type = NLA_U32},
++ [UNITEST_ARGS] = {.type = NLA_BINARY,
++ .len = MAX_UNITEST_MEMORY_LEN},
++};
++
++/**
++ * ath11k_unit_test() - send unit test command
++ * @wiphy: wiphy structure pointer
++ * @wdev: Wireless device structure pointer
++ * @data: Pointer to the data received
++ * @data_len: Length of @data
++ *
++ * Return: 0 on success; errno on failure
++ */
++
++int ath11k_unit_test(struct wiphy *wiphy, struct wireless_dev *wdev,
++ const void *data, int data_len)
++{
++ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++ struct ath11k *ar = hw->priv;
++ struct ath11k_base *ab = ar->ab;
++ struct nlattr *tb[UNITEST_MAX + 1];
++ int status = 0;
++ u32 module_id, num_args, temp_num_args, len;
++ bool sta_found = false;
++ struct ath11k_vif *arvif;
++ struct unit_test_cmd utest_cmd = {0};
++
++ list_for_each_entry(arvif, &ar->arvifs, list) {
++ if (arvif->vdev_type != WMI_VDEV_TYPE_STA ||
++ arvif->vdev_subtype != WMI_VDEV_SUBTYPE_NONE)
++ continue;
++ sta_found = true;
++ break;
++ }
++ if (!sta_found) {
++ ath11k_warn(ar->ab, "no sta found.");
++ return -EINVAL;
++ }
++
++ utest_cmd.vdev_id = arvif->vdev_id;
++
++ if (nla_parse(tb, UNITEST_MAX, data, data_len, ath11k_unitest_policy, NULL)) {
++ ath11k_warn(ab, "Invalid ATTR");
++ return -EINVAL;
++ }
++
++ if (!tb[UNITEST_MODULE_ID]) {
++ ath11k_warn(ab, "attr unitest module id failed");
++ return -EINVAL;
++ }
++ module_id = nla_get_u32(tb[UNITEST_MODULE_ID]);
++ utest_cmd.module_id = module_id;
++
++ if (!tb[UNITEST_ARGS_NUM]) {
++ ath11k_warn(ab, "attr unitest args num failed");
++ return -EINVAL;
++ }
++ num_args = nla_get_u32(tb[UNITEST_ARGS_NUM]);
++ utest_cmd.num_args = num_args;
++ if (num_args > UNIT_TEST_MAX_NUM_ARGS) {
++ ath11k_warn(ab, "num args exceed");
++ return -EINVAL;
++ }
++
++ if (!tb[UNITEST_ARGS]) {
++ ath11k_warn(ab, "attr unitest args failed");
++ return -EINVAL;
++ }
++ len = nla_len(tb[UNITEST_ARGS]);
++ temp_num_args = len / sizeof(u32);
++ if (num_args != temp_num_args) {
++ ath11k_warn(ab, "num args mismatch");
++ return -EINVAL;
++ }
++ nla_memcpy(utest_cmd.args, tb[UNITEST_ARGS], len);
++
++ status = ath11k_wmi_set_unit_test(ar, &utest_cmd);
++ if (status) {
++ ath11k_warn(ab, "Unable to post unit test message (status-%d)", status);
++ return -EINVAL;
++ }
++
++ return 0;
++}
+diff --git a/drivers/net/wireless/ath/ath11k/unitest.h b/drivers/net/wireless/ath/ath11k/unitest.h
+new file mode 100644
+index 000000000..9fee1cd68
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath11k/unitest.h
+@@ -0,0 +1,44 @@
++/* SPDX-License-Identifier: BSD-3-Clause-Clear */
++/**
++ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
++ */
++
++#ifndef _UNIT_TEST_H_
++#define _UNIT_TEST_H_
++
++#define VENDOR_ID 0x001374
++#define QCA_NL80211_VENDOR_UNITEST_SUBCMD 84
++#define MAX_UNITEST_MEMORY_LEN 128
++
++enum qca_wlan_vendor_attr_unit_test {
++ QCA_WLAN_VENDOR_ATTR_UNIT_TEST_INVALID = 0,
++ QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MODULE_ID,
++ QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS_NUM,
++ QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS,
++ /* keep last */
++ QCA_WLAN_VENDOR_ATTR_UNIT_TEST_AFTER_LAST,
++ QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX =
++ QCA_WLAN_VENDOR_ATTR_UNIT_TEST_AFTER_LAST - 1,
++};
++
++#define UNITEST_INVALID QCA_WLAN_VENDOR_ATTR_UNIT_TEST_INVALID
++#define UNITEST_MODULE_ID QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MODULE_ID
++#define UNITEST_ARGS QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS
++#define UNITEST_ARGS_NUM QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS_NUM
++#define UNITEST_MAX QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX
++
++#define ath11k_unit_test_command \
++{\
++ .info.vendor_id = VENDOR_ID,\
++ .info.subcmd = QCA_NL80211_VENDOR_UNITEST_SUBCMD,\
++ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |\
++ WIPHY_VENDOR_CMD_NEED_NETDEV |\
++ WIPHY_VENDOR_CMD_NEED_RUNNING,\
++ .doit = ath11k_unit_test,\
++ .policy = VENDOR_CMD_RAW_DATA,\
++ .maxattr = QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX,\
++}
++
++int ath11k_unit_test(struct wiphy *wiphy, struct wireless_dev *wdev,
++ const void *data, int data_len);
++#endif
+diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
+index 1c07f55c2..04e9fdd69 100644
+--- a/drivers/net/wireless/ath/ath11k/wmi.c
++++ b/drivers/net/wireless/ath/ath11k/wmi.c
+@@ -2379,6 +2379,69 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
+ return ret;
+ }
+
++int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar,
++ u32 vdev_id,
++ struct ath11k_reg_tpc_power_info *param)
++{
++ struct ath11k_pdev_wmi *wmi = ar->wmi;
++ struct wmi_vdev_set_tpc_power_cmd *cmd;
++ struct wmi_vdev_ch_power_info *ch;
++ struct sk_buff *skb;
++ struct wmi_tlv *tlv;
++ u8 *ptr;
++ int i, ret, len;
++
++ len = sizeof(*cmd) + TLV_HDR_SIZE;
++ len += (sizeof(struct wmi_vdev_ch_power_info) * param->num_pwr_levels);
++
++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
++ if (!skb)
++ return -ENOMEM;
++
++ ptr = skb->data;
++
++ cmd = (struct wmi_vdev_set_tpc_power_cmd *)ptr;
++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_SET_TPC_POWER_CMD) |
++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
++ cmd->vdev_id = vdev_id;
++ cmd->psd_power = param->is_psd_power;
++ cmd->eirp_power = param->eirp_power;
++ cmd->power_type_6ghz = param->power_type_6g;
++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
++ "wmi tpc vdev_id %d is_psd_power %d eirp_power %d power_type_6g %d\n",
++ vdev_id, param->is_psd_power, param->eirp_power, param->power_type_6g);
++
++ ptr += sizeof(*cmd);
++ tlv = (struct wmi_tlv *)ptr;
++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) |
++ FIELD_PREP(WMI_TLV_LEN, param->num_pwr_levels * sizeof(*ch));
++
++ ptr += TLV_HDR_SIZE;
++ ch = (struct wmi_vdev_ch_power_info *)ptr;
++
++ for (i = 0; i < param->num_pwr_levels; i++, ch++) {
++ ch->tlv_header = FIELD_PREP(WMI_TLV_TAG,
++ WMI_TAG_VDEV_CH_POWER_INFO) |
++ FIELD_PREP(WMI_TLV_LEN,
++ sizeof(*ch) - TLV_HDR_SIZE);
++
++ ch->chan_cfreq = param->chan_power_info[i].chan_cfreq;
++ ch->tx_power = param->chan_power_info[i].tx_power;
++
++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
++ "wmi tpc chan_cfreq %d tx_power %d\n",
++ ch->chan_cfreq, ch->tx_power);
++ }
++
++ ret = ath11k_wmi_cmd_send(wmi, skb,
++ WMI_VDEV_SET_TPC_POWER_CMDID);
++ if (ret) {
++ ath11k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n");
++ dev_kfree_skb(skb);
++ }
++ return ret;
++}
++
+ int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar,
+ struct scan_cancel_param *param)
+ {
+@@ -4069,6 +4132,7 @@ ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg,
+ wmi_cfg->sched_params = tg_cfg->sched_params;
+ wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count;
+ wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count;
++ wmi_cfg->host_service_flags = tg_cfg->host_service_flags;
+ wmi_cfg->host_service_flags &=
+ ~(1 << WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT);
+ wmi_cfg->host_service_flags |= (tg_cfg->is_reg_cc_ext_event_supported <<
+@@ -4749,6 +4813,11 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc,
+ soc->pdevs[0].pdev_id = 0;
+ }
+
++ if (!soc->reg_info_store)
++ soc->reg_info_store = kcalloc(soc->num_radios,
++ sizeof(*soc->reg_info_store),
++ GFP_ATOMIC);
++
+ return 0;
+ }
+
+@@ -5028,6 +5097,7 @@ static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buf
+ vdev_rsp->mac_id = ev->mac_id;
+ vdev_rsp->cfgd_tx_streams = ev->cfgd_tx_streams;
+ vdev_rsp->cfgd_rx_streams = ev->cfgd_rx_streams;
++ vdev_rsp->max_allowed_tx_power = ev->max_allowed_tx_power;
+
+ kfree(tb);
+ return 0;
+@@ -7070,33 +7140,34 @@ static bool ath11k_reg_is_world_alpha(char *alpha)
+ return false;
+ }
+
+-static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+- struct sk_buff *skb,
+- enum wmi_reg_chan_list_cmd_type id)
++void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info)
+ {
+- struct cur_regulatory_info *reg_info = NULL;
+- struct ieee80211_regdomain *regd = NULL;
+- bool intersect = false;
+- int ret = 0, pdev_idx, i, j;
+- struct ath11k *ar;
++ int i, j;
+
+- reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
+- if (!reg_info) {
+- ret = -ENOMEM;
+- goto fallback;
+- }
++ if (reg_info) {
++ kfree(reg_info->reg_rules_2ghz_ptr);
+
+- if (id == WMI_REG_CHAN_LIST_CC_ID)
+- ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
+- else
+- ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
++ kfree(reg_info->reg_rules_5ghz_ptr);
+
+- if (ret) {
+- ath11k_warn(ab, "failed to extract regulatory info from received event\n");
+- goto fallback;
++ for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
++ kfree(reg_info->reg_rules_6ghz_ap_ptr[i]);
++ for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++)
++ kfree(reg_info->reg_rules_6ghz_client_ptr[i][j]);
++ }
++
++ memset(reg_info, 0, sizeof(*reg_info));
+ }
++}
+
+- ath11k_dbg(ab, ATH11K_DBG_WMI, "event reg chan list id %d", id);
++int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
++ struct cur_regulatory_info *reg_info,
++ enum ieee80211_ap_reg_power power_type)
++{
++ struct ieee80211_regdomain *regd;
++ bool intersect = false;
++ int pdev_idx;
++ struct ath11k *ar;
++ enum wmi_vdev_type vdev_type;
+
+ if (reg_info->status_code != REG_SET_CC_STATUS_PASS) {
+ /* In case of failure to set the requested ctry,
+@@ -7104,7 +7175,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+ * and return from here.
+ */
+ ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n");
+- goto mem_free;
++ return -EINVAL;
+ }
+
+ pdev_idx = reg_info->phy_id;
+@@ -7112,13 +7183,13 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+ /* Avoid default reg rule updates sent during FW recovery if
+ * it is already available
+ */
+- spin_lock(&ab->base_lock);
++ spin_lock_bh(&ab->base_lock);
+ if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) &&
+ ab->default_regd[pdev_idx]) {
+- spin_unlock(&ab->base_lock);
+- goto mem_free;
++ spin_unlock_bh(&ab->base_lock);
++ goto retfail;
+ }
+- spin_unlock(&ab->base_lock);
++ spin_unlock_bh(&ab->base_lock);
+
+ if (pdev_idx >= ab->num_radios) {
+ /* Process the event for phy0 only if single_pdev_only
+@@ -7127,7 +7198,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+ */
+ if (ab->hw_params.single_pdev_only &&
+ pdev_idx < ab->hw_params.num_rxmda_per_pdev)
+- goto mem_free;
++ goto retfail;
+ else
+ goto fallback;
+ }
+@@ -7138,7 +7209,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+ if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] &&
+ !memcmp((char *)ab->default_regd[pdev_idx]->alpha2,
+ (char *)reg_info->alpha2, 2))
+- goto mem_free;
++ goto retfail;
+
+ /* Intersect new rules with default regd if a new country setting was
+ * requested, i.e a default regd was already set during initialization
+@@ -7150,13 +7221,25 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+ !ath11k_reg_is_world_alpha((char *)reg_info->alpha2))
+ intersect = true;
+
+- regd = ath11k_reg_build_regd(ab, reg_info, intersect);
++ ar = ab->pdevs[pdev_idx].ar;
++ vdev_type = ath11k_mac_get_ar_vdev_type(ar);
++
++ ath11k_dbg(ab, ATH11K_DBG_WMI,
++ "wmi handle chan list power type %d vdev type %d intersect %d\n",
++ power_type, vdev_type, intersect);
++
++ regd = ath11k_reg_build_regd(ab, reg_info, intersect, vdev_type, power_type);
+ if (!regd) {
+ ath11k_warn(ab, "failed to build regd from reg_info\n");
+ goto fallback;
+ }
+
+- spin_lock(&ab->base_lock);
++ if (power_type == IEEE80211_REG_UNSET_AP) {
++ ath11k_reg_reset_info(&ab->reg_info_store[pdev_idx]);
++ ab->reg_info_store[pdev_idx] = *reg_info;
++ }
++
++ spin_lock_bh(&ab->base_lock);
+ if (ab->default_regd[pdev_idx]) {
+ /* The initial rules from FW after WMI Init is to build
+ * the default regd. From then on, any rules updated for
+@@ -7176,9 +7259,9 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+ ab->default_regd[pdev_idx] = regd;
+ }
+ ab->dfs_region = reg_info->dfs_region;
+- spin_unlock(&ab->base_lock);
++ spin_unlock_bh(&ab->base_lock);
+
+- goto mem_free;
++ return 0;
+
+ fallback:
+ /* Fallback to older reg (by sending previous country setting
+@@ -7190,20 +7273,45 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
+ */
+ /* TODO: This is rare, but still should also be handled */
+ WARN_ON(1);
+-mem_free:
+- if (reg_info) {
+- kfree(reg_info->reg_rules_2ghz_ptr);
+- kfree(reg_info->reg_rules_5ghz_ptr);
+- if (reg_info->is_ext_reg_event) {
+- for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++)
+- kfree(reg_info->reg_rules_6ghz_ap_ptr[i]);
+
+- for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++)
+- for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++)
+- kfree(reg_info->reg_rules_6ghz_client_ptr[j][i]);
+- }
+- kfree(reg_info);
++retfail:
++
++ return -EINVAL;
++}
++
++static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb,
++ enum wmi_reg_chan_list_cmd_type id)
++{
++ struct cur_regulatory_info *reg_info;
++ int ret;
++
++ reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
++ if (!reg_info)
++ return -ENOMEM;
++
++ if (id == WMI_REG_CHAN_LIST_CC_ID)
++ ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
++ else
++ ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
++
++ if (ret) {
++ ath11k_warn(ab, "failed to extract regulatory info from received event\n");
++ goto mem_free;
++ }
++
++ ret = ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_UNSET_AP);
++ if (ret) {
++ ath11k_dbg(ab, ATH11K_DBG_WMI,
++ "failed to process regulatory info from received event\n");
++ goto mem_free;
+ }
++
++ kfree(reg_info);
++ return 0;
++
++mem_free:
++ ath11k_reg_reset_info(reg_info);
++ kfree(reg_info);
+ return ret;
+ }
+
+@@ -7363,7 +7471,7 @@ static void ath11k_vdev_start_resp_event(struct ath11k_base *ab, struct sk_buff
+ }
+
+ ar->last_wmi_vdev_start_status = 0;
+-
++ ar->max_allowed_tx_power = vdev_start_resp.max_allowed_tx_power;
+ status = vdev_start_resp.status;
+
+ if (WARN_ON_ONCE(status)) {
+@@ -8308,6 +8416,37 @@ ath11k_wmi_pdev_csa_switch_count_status_event(struct ath11k_base *ab,
+ kfree(tb);
+ }
+
++static void
++ath11k_wmi_tm_event_segmented(struct ath11k_base *ab, u32 cmd_id,
++ struct sk_buff *skb)
++{
++ const void **tb;
++ const struct wmi_ftm_event_msg *ev;
++ u16 length;
++ int ret;
++
++ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
++ return;
++ }
++
++ ev = tb[WMI_TAG_ARRAY_BYTE];
++ if (!ev) {
++ ath11k_warn(ab, "failed to fetch ftm msg\n");
++ kfree(tb);
++ return;
++ }
++
++ length = skb->len - TLV_HDR_SIZE;
++ ret = ath11k_tm_process_event(ab, cmd_id, ev, length);
++ if (ret)
++ ath11k_warn(ab, "Failed to process ftm event\n");
++
++ kfree(tb);
++}
++
+ static void
+ ath11k_wmi_pdev_dfs_radar_detected_event(struct ath11k_base *ab, struct sk_buff *skb)
+ {
+@@ -8734,7 +8873,10 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
+ ath11k_wmi_pdev_csa_switch_count_status_event(ab, skb);
+ break;
+ case WMI_PDEV_UTF_EVENTID:
+- ath11k_tm_wmi_event(ab, id, skb);
++ if (test_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags))
++ ath11k_wmi_tm_event_segmented(ab, id, skb);
++ else
++ ath11k_tm_wmi_event_unsegmented(ab, id, skb);
+ break;
+ case WMI_PDEV_TEMPERATURE_EVENTID:
+ ath11k_wmi_pdev_temperature_event(ab, skb);
+@@ -9794,3 +9936,76 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
+
+ return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+ }
++
++int ath11k_wmi_set_unit_test(struct ath11k *ar, struct unit_test_cmd *unit_test)
++{
++ struct ath11k_pdev_wmi *wmi = ar->wmi;
++ struct sk_buff *skb;
++ struct wmi_unit_test_cmd_fixed_param *cmd;
++ u32 len, args_tlv_len;
++ u8 *buf_ptr;
++ u32 *args;
++ struct wmi_tlv *tlv;
++ u32 i;
++
++ args_tlv_len = TLV_HDR_SIZE + unit_test->num_args * sizeof(u32);
++
++ len = sizeof(struct wmi_unit_test_cmd_fixed_param) + args_tlv_len;
++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
++ if (!skb)
++ return -ENOMEM;
++
++ cmd = (struct wmi_unit_test_cmd_fixed_param *)skb->data;
++ buf_ptr = (u8 *)cmd;
++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
++ WMI_TAG_UNIT_TEST_CMD) |
++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
++ cmd->vdev_id = unit_test->vdev_id;
++ cmd->module_id = unit_test->module_id;
++ cmd->num_args = unit_test->num_args;
++
++ buf_ptr += sizeof(*cmd);
++
++ tlv = (struct wmi_tlv *)buf_ptr;
++ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) |
++ FIELD_PREP(WMI_TLV_LEN, unit_test->num_args * sizeof(u32));
++ args = (u32 *)(buf_ptr + TLV_HDR_SIZE);
++ ath11k_info(ar->ab, "module id = 0x%x, num args = %u",
++ unit_test->module_id, unit_test->num_args);
++ for (i = 0; (i < unit_test->num_args && i < UNIT_TEST_MAX_NUM_ARGS); i++) {
++ args[i] = unit_test->args[i];
++ ath11k_info(ar->ab, "0x%x,", unit_test->args[i]);
++ }
++
++ return ath11k_wmi_cmd_send(wmi, skb, WMI_UNIT_TEST_CMDID);
++}
++
++int ath11k_wmi_send_coex_config(struct ath11k *ar,
++ struct wmi_coex_config_params *param)
++{
++ struct ath11k_pdev_wmi *wmi = ar->wmi;
++ struct wmi_coex_config_cmd *cmd;
++ struct sk_buff *skb;
++
++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
++ if (!skb)
++ return -ENOMEM;
++
++ cmd = (struct wmi_coex_config_cmd *)skb->data;
++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_COEX_CONFIG_CMD) |
++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
++ cmd->vdev_id = param->vdev_id;
++ cmd->config_type = param->config_type;
++ cmd->config_arg1 = param->config_arg1;
++ cmd->config_arg2 = param->config_arg2;
++ cmd->config_arg3 = param->config_arg3;
++ cmd->config_arg4 = param->config_arg4;
++ cmd->config_arg5 = param->config_arg5;
++ cmd->config_arg6 = param->config_arg6;
++ ath11k_warn(ar->ab, "wmi send coex cfg vdev %d type %u args %u %u %u %u %u %u\n",
++ cmd->vdev_id, cmd->config_type, cmd->config_arg1,
++ cmd->config_arg2, cmd->config_arg3, cmd->config_arg4,
++ cmd->config_arg5, cmd->config_arg6);
++
++ return ath11k_wmi_cmd_send(wmi, skb, WMI_COEX_CONFIG_CMDID);
++}
+diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
+index 100bb816b..9b0fb96aa 100644
+--- a/drivers/net/wireless/ath/ath11k/wmi.h
++++ b/drivers/net/wireless/ath/ath11k/wmi.h
+@@ -15,6 +15,8 @@ struct ath11k;
+ struct ath11k_fw_stats;
+ struct ath11k_fw_dbglog;
+ struct ath11k_vif;
++struct ath11k_base;
++struct ath11k_reg_tpc_power_info;
+
+ #define PSOC_HOST_MAX_NUM_SS (8)
+
+@@ -327,6 +329,36 @@ enum wmi_tlv_cmd_id {
+ WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID,
+ WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID,
+ WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID,
++ /** WMI commands related to dbg arp stats */
++ WMI_VDEV_SET_ARP_STAT_CMDID,
++ WMI_VDEV_GET_ARP_STAT_CMDID,
++ /** get tx power for the current vdev */
++ WMI_VDEV_GET_TX_POWER_CMDID,
++ /* limit STA offchannel activity */
++ WMI_VDEV_LIMIT_OFFCHAN_CMDID,
++ /** To set custom software retries per-AC for vdev */
++ WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID,
++ /** To set chainmask configuration for vdev */
++ WMI_VDEV_CHAINMASK_CONFIG_CMDID,
++ WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID,
++ /* request LTE-Coex info */
++ WMI_VDEV_GET_MWS_COEX_INFO_CMDID,
++ /** delete all peer (excluding bss peer) */
++ WMI_VDEV_DELETE_ALL_PEER_CMDID,
++ /* To set bss max idle time related parameters */
++ WMI_VDEV_BSS_MAX_IDLE_TIME_CMDID,
++ /** Indicates firmware to trigger Audio sync */
++ WMI_VDEV_AUDIO_SYNC_TRIGGER_CMDID,
++ /** Gives Qtimer value to firmware */
++ WMI_VDEV_AUDIO_SYNC_QTIMER_CMDID,
++ /** Preferred channel list for each vdev */
++ WMI_VDEV_SET_PCL_CMDID,
++ /** VDEV_GET_BIG_DATA_CMD IS DEPRECATED - DO NOT USE */
++ WMI_VDEV_GET_BIG_DATA_CMDID,
++ /** Get per vdev BIG DATA stats phase 2 */
++ WMI_VDEV_GET_BIG_DATA_P2_CMDID,
++ /** set TPC PSD/non-PSD power */
++ WMI_VDEV_SET_TPC_POWER_CMDID,
+ WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER),
+ WMI_PEER_DELETE_CMDID,
+ WMI_PEER_FLUSH_TIDS_CMDID,
+@@ -1878,6 +1910,8 @@ enum wmi_tlv_tag {
+ WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD,
+ WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9,
+ WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT,
++ WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5,
++ WMI_TAG_VDEV_CH_POWER_INFO,
+ WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8,
+ WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD,
+ WMI_TAG_MAX
+@@ -2112,6 +2146,7 @@ enum wmi_tlv_service {
+ /* The second 128 bits */
+ WMI_MAX_EXT_SERVICE = 256,
+ WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL = 265,
++ WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT = 280,
+ WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281,
+ WMI_TLV_SERVICE_BIOS_SAR_SUPPORT = 326,
+ WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN = 357,
+@@ -2330,6 +2365,7 @@ struct wmi_init_cmd {
+ #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5)
+ #define WMI_RSRC_CFG_FLAG2_CALC_NEXT_DTIM_COUNT_SET BIT(9)
+ #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18)
++#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_NAN_IFACE_SUPPORT BIT(0)
+
+ #define WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT 4
+
+@@ -2393,6 +2429,7 @@ struct wmi_resource_config {
+ u32 twt_ap_pdev_count;
+ u32 twt_ap_sta_count;
+ u32 max_nlo_ssids;
++ u32 num_packet_filters;
+ u32 num_pkt_filters;
+ u32 num_max_sta_vdevs;
+ u32 max_bssid_indicator;
+@@ -3167,6 +3204,31 @@ struct wlan_ssid {
+ u8 ssid[WLAN_SSID_MAX_LEN];
+ };
+
++struct wmi_vdev_ch_power_info {
++ u32 tlv_header;
++ u32 chan_cfreq; /* Channel center frequency (MHz) */
++ /* Unit: dBm, either PSD/EIRP power for this frequency or
++ * incremental for non-PSD BW
++ */
++ u32 tx_power;
++} __packed;
++
++struct wmi_vdev_set_tpc_power_cmd {
++ u32 tlv_header;
++ u32 vdev_id;
++ u32 psd_power; /* Value: 0 or 1, is PSD power or not */
++ u32 eirp_power; /* Maximum EIRP power (dBm units), valid only if power is PSD */
++ u32 power_type_6ghz; /* Type: WMI_6GHZ_REG_TYPE, used for halphy CTL lookup */
++ /* This fixed_param TLV is followed by the below TLVs:
++ * num_pwr_levels of wmi_vdev_ch_power_info
++ * For PSD power, it is the PSD/EIRP power of the frequency (20 MHz chunks).
++ * For non-PSD power, the power values are for 20, 40, and till
++ * BSS BW power levels.
++ * The num_pwr_levels will be checked by sw how many elements present
++ * in the variable-length array.
++ */
++} __packed;
++
+ #define WMI_IE_BITMAP_SIZE 8
+
+ /* prefix used by scan requestor ids on the host */
+@@ -3765,6 +3827,7 @@ struct wmi_stop_scan_cmd {
+ };
+
+ struct scan_chan_list_params {
++ struct list_head list;
+ u32 pdev_id;
+ u16 nallchans;
+ struct channel_param ch_param[];
+@@ -4143,6 +4206,7 @@ struct wmi_vdev_start_resp_event {
+ };
+ u32 cfgd_tx_streams;
+ u32 cfgd_rx_streams;
++ s32 max_allowed_tx_power;
+ } __packed;
+
+ /* VDEV start response status codes */
+@@ -4975,6 +5039,7 @@ struct ath11k_targ_cap {
+ };
+
+ enum wmi_vdev_type {
++ WMI_VDEV_TYPE_UNSPEC = 0,
+ WMI_VDEV_TYPE_AP = 1,
+ WMI_VDEV_TYPE_STA = 2,
+ WMI_VDEV_TYPE_IBSS = 3,
+@@ -5695,6 +5760,15 @@ struct target_resource_config {
+ u32 use_pdev_id;
+ u32 peer_map_unmap_v2_support;
+ u32 sched_params;
++ u32 max_nlo_ssids;
++ u32 num_packet_filters;
++ u32 num_max_sta_vdevs;
++ u32 max_bssid_indicator;
++ u32 ul_resp_config;
++ u32 msdu_flow_override_config0;
++ u32 msdu_flow_override_config1;
++ u32 flags2;
++ u32 host_service_flags;
+ u32 twt_ap_pdev_count;
+ u32 twt_ap_sta_count;
+ u8 is_reg_cc_ext_event_supported;
+@@ -6320,6 +6394,105 @@ enum wmi_sta_keepalive_method {
+ #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30
+ #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
+
++#define UNIT_TEST_MAX_NUM_ARGS 8
++
++struct unit_test_cmd {
++ u32 vdev_id;
++ u32 module_id;
++ u32 num_args;
++ u32 args[UNIT_TEST_MAX_NUM_ARGS];
++};
++
++struct wmi_unit_test_cmd_fixed_param {
++ u32 tlv_header;
++ u32 vdev_id;
++ u32 module_id;
++ u32 num_args;
++ u32 diag_token;
++ /**
++ * TLV (tag length value) parameters follow the wmi_unit_test_cmd_fixed_param
++ * structure. The TLV's are:
++ * u32 args[];
++ */
++} __packed;
++
++enum wmi_coex_config_type {
++ WMI_COEX_CONFIG_PAGE_P2P_TDM = 1,
++ WMI_COEX_CONFIG_PAGE_STA_TDM = 2,
++ WMI_COEX_CONFIG_PAGE_SAP_TDM = 3,
++ WMI_COEX_CONFIG_DURING_WLAN_CONN = 4,
++ WMI_COEX_CONFIG_BTC_ENABLE = 5,
++ WMI_COEX_CONFIG_COEX_DBG = 6,
++ WMI_COEX_CONFIG_PAGE_P2P_STA_TDM = 7,
++ WMI_COEX_CONFIG_INQUIRY_P2P_TDM = 8,
++ WMI_COEX_CONFIG_INQUIRY_STA_TDM = 9,
++ WMI_COEX_CONFIG_INQUIRY_SAP_TDM = 10,
++ WMI_COEX_CONFIG_INQUIRY_P2P_STA_TDM = 11,
++ WMI_COEX_CONFIG_TX_POWER = 12,
++ WMI_COEX_CONFIG_PTA_CONFIG = 13,
++ WMI_COEX_CONFIG_AP_TDM = 14,
++ WMI_COEX_CONFIG_WLAN_SCAN_PRIORITY = 15,
++ WMI_COEX_CONFIG_WLAN_PKT_PRIORITY = 16,
++ WMI_COEX_CONFIG_PTA_INTERFACE = 17,
++ WMI_COEX_CONFIG_BTC_DUTYCYCLE = 18,
++ WMI_COEX_CONFIG_HANDOVER_RSSI = 19,
++ WMI_COEX_CONFIG_PTA_BT_INFO = 20,
++ WMI_COEX_CONFIG_SINK_WLAN_TDM = 21,
++ WMI_COEX_CONFIG_COEX_ENABLE_MCC_TDM = 22,
++ WMI_COEX_CONFIG_LOWRSSI_A2DPOPP_TDM = 23,
++ WMI_COEX_CONFIG_BTC_MODE = 24,
++ WMI_COEX_CONFIG_ANTENNA_ISOLATION = 25,
++ WMI_COEX_CONFIG_BT_LOW_RSSI_THRESHOLD = 26,
++ WMI_COEX_CONFIG_BT_INTERFERENCE_LEVEL = 27,
++ WMI_COEX_CONFIG_WLAN_OVER_ZBLOW = 28,
++ WMI_COEX_CONFIG_WLAN_MGMT_OVER_BT_A2DP = 29,
++ WMI_COEX_CONFIG_WLAN_CONN_OVER_LE = 30,
++ WMI_COEX_CONFIG_LE_OVER_WLAN_TRAFFIC = 31,
++ WMI_COEX_CONFIG_THREE_WAY_COEX_RESET = 32,
++ WMI_COEX_CONFIG_THREE_WAY_DELAY_PARA = 33,
++ WMI_COEX_CONFIG_THREE_WAY_COEX_START = 34,
++ WMI_COEX_CONFIG_MPTA_HELPER_ENABLE = 35,
++ WMI_COEX_CONFIG_MPTA_HELPER_ZIGBEE_STATE = 36,
++ WMI_COEX_CONFIG_MPTA_HELPER_INT_OCS_PARAMS = 37,
++ WMI_COEX_CONFIG_MPTA_HELPER_MON_OCS_PARAMS = 38,
++ WMI_COEX_CONFIG_MPTA_HELPER_INT_MON_DURATION = 39,
++ WMI_COEX_CONFIG_MPTA_HELPER_ZIGBEE_CHANNEL = 40,
++ WMI_COEX_CONFIG_MPTA_HELPER_WLAN_MUTE_DURATION = 41,
++ WMI_COEX_CONFIG_BT_SCO_ALLOW_WLAN_2G_SCAN = 42,
++ WMI_COEX_CONFIG_ENABLE_2ND_HARMONIC_WAR = 43,
++ WMI_COEX_CONFIG_BTCOEX_SEPARATE_CHAIN_MODE = 44,
++ WMI_COEX_CONFIG_ENABLE_TPUT_SHAPING = 45,
++ WMI_COEX_CONFIG_ENABLE_TXBF = 46,
++ WMI_COEX_CONFIG_FORCED_ALGO = 47,
++ WMI_COEX_CONFIG_LE_SCAN_POLICY = 48,
++};
++
++struct wmi_coex_config_params {
++ u32 vdev_id;
++ u32 config_type;
++ u32 config_arg1;
++ u32 config_arg2;
++ u32 config_arg3;
++ u32 config_arg4;
++ u32 config_arg5;
++ u32 config_arg6;
++};
++
++struct wmi_coex_config_cmd {
++ u32 tlv_header;
++ u32 vdev_id;
++ u32 config_type;
++ u32 config_arg1;
++ u32 config_arg2;
++ u32 config_arg3;
++ u32 config_arg4;
++ u32 config_arg5;
++ u32 config_arg6;
++} __packed;
++
++#define WMI_COEX_ISOLATION_ARG1_DEFAUT 30
++#define WMI_COEX_BTC_MODE_ARG1_DEFAULT 1
++
+ const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr,
+ size_t len, gfp_t gfp);
+ int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
+@@ -6483,6 +6656,8 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
+ const u8 mac_addr[ETH_ALEN]);
+ int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap,
+ struct ath11k_fw_dbglog *dbglog);
++int ath11k_wmi_set_unit_test(struct ath11k *ar, struct unit_test_cmd *unit_test);
++int ath11k_wmi_send_coex_config(struct ath11k *ar, struct wmi_coex_config_params *param);
+ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
+ struct wmi_pno_scan_req *pno_scan);
+ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id);
+@@ -6505,4 +6680,11 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar);
+ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
+ const struct wmi_sta_keepalive_arg *arg);
+
++void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info);
++int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
++ struct cur_regulatory_info *reg_info,
++ enum ieee80211_ap_reg_power power_type);
++int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar,
++ u32 vdev_id,
++ struct ath11k_reg_tpc_power_info *param);
+ #endif
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index 153a8c3e7..a8ef1aead 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -115,6 +115,8 @@ struct wiphy;
+ * This may be due to the driver or due to regulatory bandwidth
+ * restrictions.
+ * @IEEE80211_CHAN_NO_EHT: EHT operation is not permitted on this channel.
++ * @IEEE80211_CHAN_PSD: power spectral density (in dBm)
++ * on this channel
+ */
+ enum ieee80211_channel_flags {
+ IEEE80211_CHAN_DISABLED = 1<<0,
+@@ -138,6 +140,7 @@ enum ieee80211_channel_flags {
+ IEEE80211_CHAN_16MHZ = 1<<18,
+ IEEE80211_CHAN_NO_320MHZ = 1<<19,
+ IEEE80211_CHAN_NO_EHT = 1<<20,
++ IEEE80211_CHAN_PSD = 1<<21,
+ };
+
+ #define IEEE80211_CHAN_NO_HT40 \
+@@ -171,6 +174,7 @@ enum ieee80211_channel_flags {
+ * on this channel.
+ * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
+ * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
++ * @psd: power spectral density (in dBm)
+ */
+ struct ieee80211_channel {
+ enum nl80211_band band;
+@@ -187,6 +191,7 @@ struct ieee80211_channel {
+ enum nl80211_dfs_state dfs_state;
+ unsigned long dfs_state_entered;
+ unsigned int dfs_cac_ms;
++ s8 psd;
+ };
+
+ /**
+diff --git a/include/net/regulatory.h b/include/net/regulatory.h
+index b2cb4a9eb..ebf9e028d 100644
+--- a/include/net/regulatory.h
++++ b/include/net/regulatory.h
+@@ -213,6 +213,7 @@ struct ieee80211_reg_rule {
+ u32 flags;
+ u32 dfs_cac_ms;
+ bool has_wmm;
++ s8 psd;
+ };
+
+ struct ieee80211_regdomain {
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index 88eb85c63..d9e5a4276 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -4213,6 +4213,8 @@ enum nl80211_wmm_rule {
+ * as the primary or any of the secondary channels isn't possible
+ * @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel
+ * in current regulatory domain.
++ * @NL80211_FREQUENCY_ATTR_PSD: power spectral density (in dBm)
++ * is allowed on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ * currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+@@ -4251,6 +4253,7 @@ enum nl80211_frequency_attr {
+ NL80211_FREQUENCY_ATTR_16MHZ,
+ NL80211_FREQUENCY_ATTR_NO_320MHZ,
+ NL80211_FREQUENCY_ATTR_NO_EHT,
++ NL80211_FREQUENCY_ATTR_PSD,
+
+ /* keep last */
+ __NL80211_FREQUENCY_ATTR_AFTER_LAST,
+@@ -4351,6 +4354,8 @@ enum nl80211_reg_type {
+ * a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * If not present or 0 default CAC time will be used.
++ * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm).
++ * This could be negative.
+ * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
+ * currently defined
+ * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
+@@ -4368,6 +4373,8 @@ enum nl80211_reg_rule_attr {
+
+ NL80211_ATTR_DFS_CAC_TIME,
+
++ NL80211_ATTR_POWER_RULE_PSD,
++
+ /* keep last */
+ __NL80211_REG_RULE_ATTR_AFTER_LAST,
+ NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
+@@ -4450,6 +4457,7 @@ enum nl80211_sched_scan_match_attr {
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
+ * @NL80211_RRF_NO_HE: HE operation not allowed
+ * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
++ * @NL80211_RRF_PSD: channels has power spectral density value
+ * @NL80211_RRF_NO_EHT: EHT operation not allowed
+ */
+ enum nl80211_reg_rule_flags {
+@@ -4470,7 +4478,8 @@ enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_160MHZ = 1<<16,
+ NL80211_RRF_NO_HE = 1<<17,
+ NL80211_RRF_NO_320MHZ = 1<<18,
+- NL80211_RRF_NO_EHT = 1<<19,
++ NL80211_RRF_PSD = 1<<19,
++ NL80211_RRF_NO_EHT = 1<<20,
+ };
+
+ #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
+diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
+index 9596ae1aa..26065fe35 100644
+--- a/kernel/dma/direct.c
++++ b/kernel/dma/direct.c
+@@ -317,6 +317,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
+ __dma_direct_free_pages(dev, page, size);
+ return NULL;
+ }
++EXPORT_SYMBOL(dma_direct_alloc);
+
+ void dma_direct_free(struct device *dev, size_t size,
+ void *cpu_addr, dma_addr_t dma_addr, unsigned long attrs)
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 931a03f45..5f64d3716 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -1115,6 +1115,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_OFFSET, chan->freq_offset))
+ goto nla_put_failure;
+
++ if ((chan->flags & IEEE80211_CHAN_PSD) &&
++ nla_put_s8(msg, NL80211_FREQUENCY_ATTR_PSD, chan->psd))
++ goto nla_put_failure;
++
+ if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
+ goto nla_put_failure;
+@@ -8578,6 +8582,11 @@ static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom,
+ reg_rule->dfs_cac_ms))
+ goto nla_put_failure;
+
++ if ((reg_rule->flags & NL80211_RRF_PSD) &&
++ nla_put_s8(msg, NL80211_ATTR_POWER_RULE_PSD,
++ reg_rule->psd))
++ goto nla_put_failure;
++
+ nla_nest_end(msg, nl_reg_rule);
+ }
+
+@@ -8751,6 +8760,7 @@ static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] =
+ [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
+ [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+ [NL80211_ATTR_DFS_CAC_TIME] = { .type = NLA_U32 },
++ [NL80211_ATTR_POWER_RULE_PSD] = { .type = NLA_S8 },
+ };
+
+ static int parse_reg_rule(struct nlattr *tb[],
+@@ -8772,6 +8782,14 @@ static int parse_reg_rule(struct nlattr *tb[],
+
+ reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
+
++ if (reg_rule->flags & NL80211_RRF_PSD) {
++ if (!tb[NL80211_ATTR_POWER_RULE_PSD])
++ return -EINVAL;
++
++ reg_rule->psd =
++ nla_get_s8(tb[NL80211_ATTR_POWER_RULE_PSD]);
++ }
++
+ freq_range->start_freq_khz =
+ nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
+ freq_range->end_freq_khz =
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 0317cf9da..e60861610 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -1589,6 +1589,8 @@ static u32 map_regdom_flags(u32 rd_flags)
+ channel_flags |= IEEE80211_CHAN_NO_320MHZ;
+ if (rd_flags & NL80211_RRF_NO_EHT)
+ channel_flags |= IEEE80211_CHAN_NO_EHT;
++ if (rd_flags & NL80211_RRF_PSD)
++ channel_flags |= IEEE80211_CHAN_PSD;
+ return channel_flags;
+ }
+
+@@ -1795,6 +1797,9 @@ static void handle_channel_single_rule(struct wiphy *wiphy,
+ chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+ }
+
++ if (chan->flags & IEEE80211_CHAN_PSD)
++ chan->psd = reg_rule->psd;
++
+ return;
+ }
+
+@@ -1815,6 +1820,9 @@ static void handle_channel_single_rule(struct wiphy *wiphy,
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ }
+
++ if (chan->flags & IEEE80211_CHAN_PSD)
++ chan->psd = reg_rule->psd;
++
+ if (chan->orig_mpwr) {
+ /*
+ * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER
+@@ -1884,6 +1892,12 @@ static void handle_channel_adjacent_rules(struct wiphy *wiphy,
+ rrule2->dfs_cac_ms);
+ }
+
++ if ((rrule1->flags & NL80211_RRF_PSD) &&
++ (rrule2->flags & NL80211_RRF_PSD))
++ chan->psd = min_t(s8, rrule1->psd, rrule2->psd);
++ else
++ chan->flags &= ~NL80211_RRF_PSD;
++
+ return;
+ }
+
+@@ -2577,6 +2591,9 @@ static void handle_channel_custom(struct wiphy *wiphy,
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ }
+
++ if (chan->flags & IEEE80211_CHAN_PSD)
++ chan->psd = reg_rule->psd;
++
+ chan->max_power = chan->max_reg_power;
+ }
+
+--
+2.43.0
+
diff --git a/SOURCES/uinput.patch b/SOURCES/uinput.patch
new file mode 100644
index 0000000..c5666a8
--- /dev/null
+++ b/SOURCES/uinput.patch
@@ -0,0 +1,133 @@
+---
+ drivers/input/misc/uinput.c | 48 +++++++++++++++++++++++++------------
+ include/uapi/linux/uinput.h | 5 ++++
+ 2 files changed, 38 insertions(+), 15 deletions(-)
+
+
+diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
+index 84051f20b18a..2c3180370a02 100644
+--- a/drivers/input/misc/uinput.c
++++ b/drivers/input/misc/uinput.c
+@@ -20,6 +20,7 @@
+ */
+ #include <uapi/linux/uinput.h>
+ #include <linux/poll.h>
++#include <linux/printk.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+ #include <linux/module.h>
+@@ -280,7 +281,7 @@ static int uinput_dev_flush(struct input_dev *dev, struct file *file)
+
+ static void uinput_destroy_device(struct uinput_device *udev)
+ {
+- const char *name, *phys;
++ const char *name, *phys, *uniq;
+ struct input_dev *dev = udev->dev;
+ enum uinput_state old_state = udev->state;
+
+@@ -289,6 +290,7 @@ static void uinput_destroy_device(struct uinput_device *udev)
+ if (dev) {
+ name = dev->name;
+ phys = dev->phys;
++ uniq = dev->uniq;
+ if (old_state == UIST_CREATED) {
+ uinput_flush_requests(udev);
+ input_unregister_device(dev);
+@@ -297,6 +299,7 @@ static void uinput_destroy_device(struct uinput_device *udev)
+ }
+ kfree(name);
+ kfree(phys);
++ kfree(uniq);
+ udev->dev = NULL;
+ }
+ }
+@@ -831,6 +834,24 @@ static int uinput_str_to_user(void __user *dest, const char *str,
+ return ret ? -EFAULT : len;
+ }
+
++static int uinput_get_user_str(struct uinput_device *udev, const char **kptr,
++ const char *uptr, unsigned int size)
++{
++ char *tmp;
++
++ if (udev->state == UIST_CREATED)
++ return -EINVAL;
++
++ tmp = strndup_user(uptr, size);
++ if (IS_ERR(tmp))
++ return PTR_ERR(tmp);
++
++ kfree(*kptr);
++ *kptr = tmp;
++
++ return 0;
++}
++
+ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
+ unsigned long arg, void __user *p)
+ {
+@@ -839,7 +860,6 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
+ struct uinput_ff_upload ff_up;
+ struct uinput_ff_erase ff_erase;
+ struct uinput_request *req;
+- char *phys;
+ const char *name;
+ unsigned int size;
+
+@@ -916,19 +936,8 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
+ goto out;
+
+ case UI_SET_PHYS:
+- if (udev->state == UIST_CREATED) {
+- retval = -EINVAL;
+- goto out;
+- }
+-
+- phys = strndup_user(p, 1024);
+- if (IS_ERR(phys)) {
+- retval = PTR_ERR(phys);
+- goto out;
+- }
+-
+- kfree(udev->dev->phys);
+- udev->dev->phys = phys;
++ pr_warn_once("uinput: UI_SET_PHYS is deprecated. Use UI_SET_PHYS_STR");
++ retval = uinput_get_user_str(udev, &udev->dev->phys, p, 1024);
+ goto out;
+
+ case UI_BEGIN_FF_UPLOAD:
+@@ -1023,6 +1032,15 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
+ case UI_ABS_SETUP & ~IOCSIZE_MASK:
+ retval = uinput_abs_setup(udev, p, size);
+ goto out;
++
++ case UI_SET_PHYS_STR(0):
++ retval = uinput_get_user_str(udev, &udev->dev->phys, p, size);
++ goto out;
++
++ case UI_SET_UNIQ_STR(0):
++ retval = uinput_get_user_str(udev, &udev->dev->uniq, p, size);
++ goto out;
++
+ }
+
+ retval = -EINVAL;
+diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
+index c9e677e3af1d..84d4fa142830 100644
+--- a/include/uapi/linux/uinput.h
++++ b/include/uapi/linux/uinput.h
+@@ -142,9 +142,14 @@ struct uinput_abs_setup {
+ #define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int)
+ #define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int)
+ #define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
++
++/* DEPRECATED: Data size is ambiguous. Use UI_SET_PHYS_STR instead. */
+ #define UI_SET_PHYS _IOW(UINPUT_IOCTL_BASE, 108, char*)
++
+ #define UI_SET_SWBIT _IOW(UINPUT_IOCTL_BASE, 109, int)
+ #define UI_SET_PROPBIT _IOW(UINPUT_IOCTL_BASE, 110, int)
++#define UI_SET_PHYS_STR(len) _IOC(_IOC_WRITE, UINPUT_IOCTL_BASE, 111, len)
++#define UI_SET_UNIQ_STR(len) _IOC(_IOC_WRITE, UINPUT_IOCTL_BASE, 112, len)
+
+ #define UI_BEGIN_FF_UPLOAD _IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload)
+ #define UI_END_FF_UPLOAD _IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload)
diff --git a/SOURCES/v10-0001-HID-asus-fix-more-n-key-report-descriptors-if-n-.patch b/SOURCES/v10-0001-HID-asus-fix-more-n-key-report-descriptors-if-n-.patch
new file mode 100644
index 0000000..61107eb
--- /dev/null
+++ b/SOURCES/v10-0001-HID-asus-fix-more-n-key-report-descriptors-if-n-.patch
@@ -0,0 +1,89 @@
+From f311b6a3ff32c8221a0003c4a154aecbf04fb12c Mon Sep 17 00:00:00 2001
+From: "Luke D. Jones" <luke@ljones.dev>
+Date: Sat, 2 Dec 2023 17:27:23 +1300
+Subject: [PATCH v10 1/4] HID: asus: fix more n-key report descriptors if n-key
+ quirked
+
+Adjusts the report descriptor for N-Key devices to
+make the output count 0x01 which completely avoids
+the need for a block of filtering.
+
+Signed-off-by: Luke D. Jones <luke@ljones.dev>
+---
+ drivers/hid/hid-asus.c | 49 ++++++++++++++++++++----------------------
+ 1 file changed, 23 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
+index 78cdfb8b9a7a..855972a4470f 100644
+--- a/drivers/hid/hid-asus.c
++++ b/drivers/hid/hid-asus.c
+@@ -335,36 +335,20 @@ static int asus_raw_event(struct hid_device *hdev,
+ if (drvdata->quirks & QUIRK_MEDION_E1239T)
+ return asus_e1239t_event(drvdata, data, size);
+
+- if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) {
++ /*
++ * Skip these report ID, the device emits a continuous stream associated
++ * with the AURA mode it is in which looks like an 'echo'.
++ */
++ if (report->id == FEATURE_KBD_LED_REPORT_ID1 || report->id == FEATURE_KBD_LED_REPORT_ID2)
++ return -1;
++ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
+ /*
+- * Skip these report ID, the device emits a continuous stream associated
+- * with the AURA mode it is in which looks like an 'echo'.
++ * G713 and G733 send these codes on some keypresses, depending on
++ * the key pressed it can trigger a shutdown event if not caught.
+ */
+- if (report->id == FEATURE_KBD_LED_REPORT_ID1 ||
+- report->id == FEATURE_KBD_LED_REPORT_ID2) {
++ if(data[0] == 0x02 && data[1] == 0x30) {
+ return -1;
+- /* Additional report filtering */
+- } else if (report->id == FEATURE_KBD_REPORT_ID) {
+- /*
+- * G14 and G15 send these codes on some keypresses with no
+- * discernable reason for doing so. We'll filter them out to avoid
+- * unmapped warning messages later.
+- */
+- if (data[1] == 0xea || data[1] == 0xec || data[1] == 0x02 ||
+- data[1] == 0x8a || data[1] == 0x9e) {
+- return -1;
+- }
+ }
+- if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
+- /*
+- * G713 and G733 send these codes on some keypresses, depending on
+- * the key pressed it can trigger a shutdown event if not caught.
+- */
+- if(data[0] == 0x02 && data[1] == 0x30) {
+- return -1;
+- }
+- }
+-
+ }
+
+ if (drvdata->quirks & QUIRK_ROG_CLAYMORE_II_KEYBOARD) {
+@@ -1250,6 +1234,19 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ rdesc[205] = 0x01;
+ }
+
++ /* match many more n-key devices */
++ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
++ for (int i = 0; i < *rsize + 1; i++) {
++ /* offset to the count from 0x5a report part always 14 */
++ if (rdesc[i] == 0x85 && rdesc[i + 1] == 0x5a &&
++ rdesc[i + 14] == 0x95 && rdesc[i + 15] == 0x05) {
++ hid_info(hdev, "Fixing up Asus N-Key report descriptor\n");
++ rdesc[i + 15] = 0x01;
++ break;
++ }
++ }
++ }
++
+ return rdesc;
+ }
+
+--
+2.41.0
+
diff --git a/SOURCES/v10-0002-HID-asus-make-asus_kbd_init-generic-remove-rog_n.patch b/SOURCES/v10-0002-HID-asus-make-asus_kbd_init-generic-remove-rog_n.patch
new file mode 100644
index 0000000..e4fb261
--- /dev/null
+++ b/SOURCES/v10-0002-HID-asus-make-asus_kbd_init-generic-remove-rog_n.patch
@@ -0,0 +1,130 @@
+From 119615a0034ee5f8d7a949c1c9d992b1be125fcb Mon Sep 17 00:00:00 2001
+From: "Luke D. Jones" <luke@ljones.dev>
+Date: Sat, 2 Dec 2023 17:47:59 +1300
+Subject: [PATCH v10 2/4] HID: asus: make asus_kbd_init() generic, remove
+ rog_nkey_led_init()
+
+Some of the n-key stuff is old and outdated, so
+make asus_kbd_init() generic to use with other
+report ID and remove rog_nkey_led_init().
+
+Signed-off-by: Luke D. Jones <luke@ljones.dev>
+---
+ drivers/hid/hid-asus.c | 70 +++++++++++-------------------------------
+ 1 file changed, 18 insertions(+), 52 deletions(-)
+
+diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
+index 855972a4470f..cdd998a761fe 100644
+--- a/drivers/hid/hid-asus.c
++++ b/drivers/hid/hid-asus.c
+@@ -1076,9 +1076,9 @@
+ return ret;
+ }
+
+-static int asus_kbd_init(struct hid_device *hdev)
++static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
+ {
+- u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
++ const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
+ 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
+ int ret;
+
+@@ -1090,9 +1090,10 @@
+ }
+
+ static int asus_kbd_get_functions(struct hid_device *hdev,
+- unsigned char *kbd_func)
++ unsigned char *kbd_func,
++ u8 report_id)
+ {
+- u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 };
++ u8 buf[] = { report_id, 0x05, 0x20, 0x31, 0x00, 0x08 };
+ u8 *readbuf;
+ int ret;
+
+@@ -1121,51 +1122,6 @@
+ return ret;
+ }
+
+-static int rog_nkey_led_init(struct hid_device *hdev)
+-{
+- u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 };
+- u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20,
+- 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
+- u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1,
+- 0x05, 0x20, 0x31, 0x00, 0x08 };
+- int ret;
+-
+- hid_info(hdev, "Asus initialise N-KEY Device");
+- /* The first message is an init start */
+- ret = asus_kbd_set_report(hdev, buf_init_start, sizeof(buf_init_start));
+- if (ret < 0) {
+- hid_warn(hdev, "Asus failed to send init start command: %d\n", ret);
+- return ret;
+- }
+- /* Followed by a string */
+- ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
+- if (ret < 0) {
+- hid_warn(hdev, "Asus failed to send init command 1.0: %d\n", ret);
+- return ret;
+- }
+- /* Followed by a string */
+- ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
+- if (ret < 0) {
+- hid_warn(hdev, "Asus failed to send init command 1.1: %d\n", ret);
+- return ret;
+- }
+-
+- /* begin second report ID with same data */
+- buf_init2[0] = FEATURE_KBD_LED_REPORT_ID2;
+- buf_init3[0] = FEATURE_KBD_LED_REPORT_ID2;
+-
+- ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
+- if (ret < 0) {
+- hid_warn(hdev, "Asus failed to send init command 2.0: %d\n", ret);
+- return ret;
+- }
+- ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
+- if (ret < 0)
+- hid_warn(hdev, "Asus failed to send init command 2.1: %d\n", ret);
+-
+- return ret;
+-}
+-
+ static void asus_schedule_work(struct asus_kbd_leds *led)
+ {
+ unsigned long flags;
+@@ -1248,17 +1204,27 @@
+ int ret;
+
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
+- ret = rog_nkey_led_init(hdev);
++ /* Initialize keyboard */
++ ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
++ if (ret < 0)
++ return ret;
++
++ /* The LED endpoint is initialised in two HID */
++ ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
++ if (ret < 0)
++ return ret;
++
++ ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* Initialize keyboard */
+- ret = asus_kbd_init(hdev);
++ ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
+ if (ret < 0)
+ return ret;
+
+ /* Get keyboard functions */
+- ret = asus_kbd_get_functions(hdev, &kbd_func);
++ ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID);
+ if (ret < 0)
+ return ret;
+
+--
+2.41.0
+
diff --git a/SOURCES/rog-ally-side-keys-fix.patch b/SOURCES/v10-0003-HID-asus-add-ROG-Ally-N-Key-ID-and-keycodes.patch
index c80387c..8cca8ca 100644
--- a/SOURCES/rog-ally-side-keys-fix.patch
+++ b/SOURCES/v10-0003-HID-asus-add-ROG-Ally-N-Key-ID-and-keycodes.patch
@@ -1,48 +1,47 @@
-From df561bb39dc0ae974bbd1d0f88b8a75cc8c20b7c Mon Sep 17 00:00:00 2001
-From: GloriousEggroll <gloriouseggroll@gmail.com>
-Date: Wed, 30 Aug 2023 20:25:07 -0600
-Subject: [PATCH 09/18] rog-ally-side-keys
+From 2b1a33a519668105eb7261c2d8964da576be6146 Mon Sep 17 00:00:00 2001
+From: "Luke D. Jones" <luke@ljones.dev>
+Date: Wed, 29 Nov 2023 22:18:11 +1300
+Subject: [PATCH v10 3/4] HID: asus: add ROG Ally N-Key ID and keycodes
---
- drivers/hid/hid-asus.c | 8 ++++++++
+ drivers/hid/hid-asus.c | 7 +++++++
drivers/hid/hid-ids.h | 1 +
- 2 files changed, 9 insertions(+)
+ 2 files changed, 8 insertions(+)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
-index fd61dba88..596196c04 100644
+index cdd998a761fe..3a1a6024d299 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
-@@ -899,6 +899,11 @@ static int asus_input_mapping(struct hid_device *hdev,
+@@ -847,6 +847,10 @@ static int asus_input_mapping(struct hid_device *hdev,
+ case 0xb3: asus_map_key_clear(KEY_PROG3); break; /* Fn+Left next aura */
+ case 0x6a: asus_map_key_clear(KEY_F13); break; /* Screenpad toggle */
case 0x4b: asus_map_key_clear(KEY_F14); break; /* Arrows/Pg-Up/Dn toggle */
-
-
+ case 0xa5: asus_map_key_clear(KEY_F15); break; /* ROG Ally left back */
+ case 0xa6: asus_map_key_clear(KEY_F16); break; /* ROG Ally QAM button */
+ case 0xa7: asus_map_key_clear(KEY_F17); break; /* ROG Ally ROG long-press */
-+ case 0xa8: asus_map_key_clear(KEY_F18); break; /* ROG Ally ROG longer-press */
-+
++ case 0xa8: asus_map_key_clear(KEY_F18); break; /* ROG Ally ROG long-press-release */
+
+
default:
- /* ASUS lazily declares 256 usages, ignore the rest,
- * as some make the keyboard appear as a pointer device. */
-@@ -1252,6 +1257,9 @@ static const struct hid_device_id asus_devices[] = {
+@@ -1239,6 +1243,9 @@ static const struct hid_device_id asus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
- USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
+ USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
+ USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY),
-+ QUIRK_ROG_NKEY_KEYBOARD },
++ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
- USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+ USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD),
+ QUIRK_ROG_CLAYMORE_II_KEYBOARD },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
-index 8a310f8ff..46f5262d7 100644
+index c6e4e0d1f214..6debff9ad678 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -208,6 +208,7 @@
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 0x1a30
-+#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY 0x1abe
++#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY 0x1abe
#define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
diff --git a/SOURCES/v10-0004-HID-asus-add-ROG-Ally-xpad-settings.patch b/SOURCES/v10-0004-HID-asus-add-ROG-Ally-xpad-settings.patch
new file mode 100644
index 0000000..5c4f5c5
--- /dev/null
+++ b/SOURCES/v10-0004-HID-asus-add-ROG-Ally-xpad-settings.patch
@@ -0,0 +1,1471 @@
+From 89b9e9a58ddfe3c6d400d08c607b50b3e2c60cbf Mon Sep 17 00:00:00 2001
+From: "Luke D. Jones" <luke@ljones.dev>
+Date: Fri, 1 Dec 2023 16:57:19 +1300
+Subject: [PATCH v10 4/4] HID: asus: add ROG Ally xpad settings
+
+- move ROG specific stuff to new .c
+- add a header for common parts
+- add xpad mode
+- add deadzones
+- add gamepad button remapping
+- add gamepad mapping reset for xpad and wasd modes
+
+Signed-off-by: Luke D. Jones <luke@ljones.dev>
+---
+ drivers/hid/Makefile | 2 +
+ drivers/hid/{hid-asus.c => hid-asus-core.c} | 71 +-
+ drivers/hid/hid-asus-rog.c | 1206 +++++++++++++++++++
+ drivers/hid/hid-asus.h | 51 +
+ 4 files changed, 1289 insertions(+), 41 deletions(-)
+ rename drivers/hid/{hid-asus.c => hid-asus-core.c} (96%)
+ create mode 100644 drivers/hid/hid-asus-rog.c
+ create mode 100644 drivers/hid/hid-asus.h
+
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
+index 8a06d0f840bc..7c2436ed05d6 100644
+--- a/drivers/hid/Makefile
++++ b/drivers/hid/Makefile
+@@ -23,6 +23,8 @@ hid-logitech-$(CONFIG_LOGIWHEELS_FF) += hid-lg4ff.o
+ hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o
+ hid-wiimote-$(CONFIG_DEBUG_FS) += hid-wiimote-debug.o
+
++hid-asus-y := hid-asus-core.o hid-asus-rog.o
++
+ obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
+ obj-$(CONFIG_HID_ACCUTOUCH) += hid-accutouch.o
+ obj-$(CONFIG_HID_ALPS) += hid-alps.o
+diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus-core.c
+similarity index 96%
+rename from drivers/hid/hid-asus.c
+rename to drivers/hid/hid-asus-core.c
+index 3a1a6024d299..026705c43ee1 100644
+--- a/drivers/hid/hid-asus.c
++++ b/drivers/hid/hid-asus-core.c
+@@ -20,9 +20,8 @@
+ * Copyright (c) 2016 Frederik Wenigwieser <frederik.wenigwieser@gmail.com>
+ */
+
+-/*
+- */
+-
++#include <asm-generic/errno-base.h>
++#include <asm-generic/errno.h>
+ #include <linux/dmi.h>
+ #include <linux/hid.h>
+ #include <linux/module.h>
+@@ -32,6 +31,7 @@
+ #include <linux/power_supply.h>
+ #include <linux/leds.h>
+
++#include "hid-asus.h"
+ #include "hid-ids.h"
+
+ MODULE_AUTHOR("Yusuke Fujimaki <usk.fujimaki@gmail.com>");
+@@ -47,10 +47,6 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
+ #define T100CHI_MOUSE_REPORT_ID 0x06
+ #define FEATURE_REPORT_ID 0x0d
+ #define INPUT_REPORT_ID 0x5d
+-#define FEATURE_KBD_REPORT_ID 0x5a
+-#define FEATURE_KBD_REPORT_SIZE 16
+-#define FEATURE_KBD_LED_REPORT_ID1 0x5d
+-#define FEATURE_KBD_LED_REPORT_ID2 0x5e
+
+ #define SUPPORT_KBD_BACKLIGHT BIT(0)
+
+@@ -71,20 +67,6 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
+ #define BATTERY_STAT_CHARGING (1)
+ #define BATTERY_STAT_FULL (2)
+
+-#define QUIRK_FIX_NOTEBOOK_REPORT BIT(0)
+-#define QUIRK_NO_INIT_REPORTS BIT(1)
+-#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
+-#define QUIRK_IS_MULTITOUCH BIT(3)
+-#define QUIRK_NO_CONSUMER_USAGES BIT(4)
+-#define QUIRK_USE_KBD_BACKLIGHT BIT(5)
+-#define QUIRK_T100_KEYBOARD BIT(6)
+-#define QUIRK_T100CHI BIT(7)
+-#define QUIRK_G752_KEYBOARD BIT(8)
+-#define QUIRK_T90CHI BIT(9)
+-#define QUIRK_MEDION_E1239T BIT(10)
+-#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)
+-#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
+-
+ #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
+ QUIRK_NO_INIT_REPORTS | \
+ QUIRK_NO_CONSUMER_USAGES)
+@@ -113,22 +95,6 @@ struct asus_touchpad_info {
+ int report_size;
+ };
+
+-struct asus_drvdata {
+- unsigned long quirks;
+- struct hid_device *hdev;
+- struct input_dev *input;
+- struct input_dev *tp_kbd_input;
+- struct asus_kbd_leds *kbd_backlight;
+- const struct asus_touchpad_info *tp;
+- bool enable_backlight;
+- struct power_supply *battery;
+- struct power_supply_desc battery_desc;
+- int battery_capacity;
+- int battery_stat;
+- bool battery_in_query;
+- unsigned long battery_next_query;
+-};
+-
+ static int asus_report_battery(struct asus_drvdata *, u8 *, int);
+
+ static const struct asus_touchpad_info asus_i2c_tp = {
+@@ -329,6 +295,16 @@ static int asus_raw_event(struct hid_device *hdev,
+ if (drvdata->battery && data[0] == BATTERY_REPORT_ID)
+ return asus_report_battery(drvdata, data, size);
+
++ // TODO: remove after debugging
++ // if (data[0] == 0x5a || data[0] == 0x5d || data[0] == 0x5e){
++ // for (int i = 0; i < size; i++) {
++ // if (i == 0)
++ // printk(KERN_INFO "GOT: %02x,", data[i]);
++ // else
++ // printk(KERN_CONT "%02x,", data[i]);
++ // }
++ // }
++
+ if (drvdata->tp && data[0] == INPUT_REPORT_ID)
+ return asus_report_input(drvdata, data, size);
+
+@@ -365,7 +341,7 @@ static int asus_raw_event(struct hid_device *hdev,
+ return 0;
+ }
+
+-static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size)
++int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size)
+ {
+ unsigned char *dmabuf;
+ int ret;
+@@ -386,6 +362,13 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
+ return ret;
+ }
+
++int asus_kbd_get_report(struct hid_device *hdev, u8 *out_buf, size_t out_buf_size)
++{
++ return hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, out_buf,
++ out_buf_size, HID_FEATURE_REPORT,
++ HID_REQ_GET_REPORT);
++}
++
+ static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
+ {
+ const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
+@@ -846,8 +829,8 @@ static int asus_input_mapping(struct hid_device *hdev,
+ case 0xb2: asus_map_key_clear(KEY_PROG2); break; /* Fn+Left previous aura */
+ case 0xb3: asus_map_key_clear(KEY_PROG3); break; /* Fn+Left next aura */
+ case 0x6a: asus_map_key_clear(KEY_F13); break; /* Screenpad toggle */
+- case 0x4b: asus_map_key_clear(KEY_F14); break; /* Arrows/Pg-Up/Dn toggle */
+- case 0xa5: asus_map_key_clear(KEY_F15); break; /* ROG Ally left back */
++ case 0x4b: asus_map_key_clear(KEY_F14); break; /* Arrows/Pg-Up/Dn toggle, Ally M1 */
++ case 0xa5: asus_map_key_clear(KEY_F15); break; /* ROG Ally M2 */
+ case 0xa6: asus_map_key_clear(KEY_F16); break; /* ROG Ally QAM button */
+ case 0xa7: asus_map_key_clear(KEY_F17); break; /* ROG Ally ROG long-press */
+ case 0xa8: asus_map_key_clear(KEY_F18); break; /* ROG Ally ROG long-press-release */
+@@ -1063,6 +1046,10 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
+ }
+ }
+
++ /* all ROG devices have this HID interface but we will focus on Ally for now */
++ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && hid_is_usb(hdev))
++ rog_ally.probe(hdev, &rog_ally);
++
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "Asus hid parse failed: %d\n", ret);
+@@ -1112,6 +1099,8 @@ static void asus_remove(struct hid_device *hdev)
+ cancel_work_sync(&drvdata->kbd_backlight->work);
+ }
+
++ rog_ally.remove(hdev, &rog_ally);
++
+ hid_hw_stop(hdev);
+ }
+
+@@ -1245,7 +1234,7 @@ static const struct hid_device_id asus_devices[] = {
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
+ USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY),
+- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
++ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_ALLY_XPAD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
+ USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD),
+ QUIRK_ROG_CLAYMORE_II_KEYBOARD },
+diff --git a/drivers/hid/hid-asus-rog.c b/drivers/hid/hid-asus-rog.c
+new file mode 100644
+index 000000000000..897c13769bae
+--- /dev/null
++++ b/drivers/hid/hid-asus-rog.c
+@@ -0,0 +1,1206 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * HID driver for Asus ROG laptops and Ally
++ *
++ * Copyright (c) 2016 Yusuke Fujimaki <usk.fujimaki@gmail.com>
++ */
++
++#include <linux/hid.h>
++#include <linux/types.h>
++#include <linux/usb.h>
++
++#include "hid-asus.h"
++
++/* 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)
++
++#define BTN_CODE_LEN 11
++#define MAPPING_BLOCK_LEN 44
++
++enum ally_xpad_mode {
++ ally_xpad_mode_game = 0x01,
++ ally_xpad_mode_wasd = 0x02,
++ ally_xpad_mode_mouse = 0x03,
++};
++
++enum ally_xpad_cmd {
++ ally_xpad_cmd_set_mode = 0x01,
++ ally_xpad_cmd_set_js_dz = 0x04, /* deadzones */
++ ally_xpad_cmd_set_tr_dz = 0x05, /* deadzones */
++ ally_xpad_cmd_check_ready = 0x0A,
++};
++
++enum ally_xpad_axis {
++ ally_xpad_axis_xy_left = 0x01,
++ ally_xpad_axis_xy_right = 0x02,
++ ally_xpad_axis_z_left = 0x03,
++ ally_xpad_axis_z_right = 0x04,
++};
++
++enum ally_out_dev {
++ ally_out_dev_blank = 0x00,
++ ally_out_dev_xpad = 0x01,
++ ally_out_dev_keyboard = 0x02,
++ ally_out_dev_mouse = 0x03,
++ ally_out_dev_macro = 0x04,
++ ally_out_dev_media = 0x05,
++};
++
++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,
++};
++
++/* ROG Ally has many settings related to the gamepad, all using the same n-key endpoint */
++struct asus_rog_ally {
++ enum ally_xpad_mode mode;
++ /*ally_xpad_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[2][4];
++ /*
++ * index: left, right
++ * max: 64
++ */
++ u8 vibration_intensity[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[2][8];
++ /*
++ * left = byte 0, right = byte 1
++ */
++ bool supports_anti_deadzones;
++ u8 anti_deadzones[2];
++
++ /*
++ * index: [mode][phys pair][b1, b1 secondary, b2, b2 secondary, blocks of 11]
++ */
++ u8 key_mapping[ally_xpad_mode_mouse][btn_pair_lt_rt][MAPPING_BLOCK_LEN];
++};
++
++#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_`"
++#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_-"
++#define KB_EQUALS "kb_="
++#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"
++//#defi KB_I neuf, "i")) out[2] = 0x1c;
++#define KB_O "kb_o"
++#define KB_P "kb_p"
++#define KB_LBRACKET "kb_["
++#define KB_RBRACKET "kb_]"
++#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_;"
++#define KB_QUOTE "kb_'"
++#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_COMA "kb_,"
++#define KB_PERIOD "kb_."
++// #defKB_uf, "/")) out[2] = 0x52; missing
++#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_LCK "numpad_lck"
++#define NUMPAD_FWDSLASH "numpad_/"
++#define NUMPAD_STAR "numpad_*"
++#define NUMPAD_HYPHEN "numpad_-"
++#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_+"
++#define NUMPAD_ENTER "numpad_enter"
++#define NUMPAD_PERIOD "numpad_."
++
++#define RAT_LCLICK "rat_lclick"
++#define RAT_RCLICK "rat_rclick"
++#define RAT_MCLICK "rat_mclick"
++#define RAT_WHEEL_UP "rat_wheel_up"
++#define RAT_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"
++
++static struct asus_rog_ally *__rog_ally_data(struct device *raw_dev)
++{
++ struct hid_device *hdev = to_hid_device(raw_dev);
++ return ((struct asus_drvdata *)hid_get_drvdata(hdev))->rog_ally_data;
++}
++
++/* writes the bytes for a requested key/function in to the out buffer */
++const static int __string_to_key_code(const char *buf, u8 *out, int out_len)
++{
++ 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
++
++ // Allow clearing
++ if (!strcmp(buf, " ") || !strcmp(buf, "\n"))
++ goto success;
++
++ // set group xpad
++ out[0] = 0x01;
++ if (!strcmp(buf, PAD_A)) out[1] = 0x01;
++ else if (!strcmp(buf, PAD_B)) out[1] = 0x02;
++ else if (!strcmp(buf, PAD_X)) out[1] = 0x03;
++ else if (!strcmp(buf, PAD_Y)) out[1] = 0x04;
++ else if (!strcmp(buf, PAD_LB)) out[1] = 0x05;
++ else if (!strcmp(buf, PAD_RB)) out[1] = 0x06;
++ else if (!strcmp(buf, PAD_LS)) out[1] = 0x07;
++ else if (!strcmp(buf, PAD_RS)) out[1] = 0x08;
++ else if (!strcmp(buf, PAD_DPAD_UP)) out[1] = 0x09;
++ else if (!strcmp(buf, PAD_DPAD_DOWN)) out[1] = 0x0a;
++ else if (!strcmp(buf, PAD_DPAD_LEFT)) out[1] = 0x0b;
++ else if (!strcmp(buf, PAD_DPAD_RIGHT)) out[1] = 0x0c;
++ else if (!strcmp(buf, PAD_VIEW)) out[1] = 0x11;
++ else if (!strcmp(buf, PAD_MENU)) out[1] = 0x12;
++ else if (!strcmp(buf, PAD_XBOX)) out[1] = 0x13;
++ if (out[1])
++ goto success;
++
++ // set group keyboard
++ out[0] = 0x02;
++ if (!strcmp(buf, KB_M1)) out[2] = 0x8f;
++ else if (!strcmp(buf, KB_M2)) out[2] = 0x8e;
++ else if (!strcmp(buf, KB_ESC)) out[2] = 0x76;
++ else if (!strcmp(buf, KB_F1)) out[2] = 0x50;
++ else if (!strcmp(buf, KB_F2)) out[2] = 0x60;
++ else if (!strcmp(buf, KB_F3)) out[2] = 0x40;
++ else if (!strcmp(buf, KB_F4)) out[2] = 0x0c;
++ else if (!strcmp(buf, KB_F5)) out[2] = 0x03;
++ else if (!strcmp(buf, KB_F6)) out[2] = 0x0b;
++ else if (!strcmp(buf, KB_F7)) out[2] = 0x80;
++ else if (!strcmp(buf, KB_F8)) out[2] = 0x0a;
++ else if (!strcmp(buf, KB_F9)) out[2] = 0x01;
++ else if (!strcmp(buf, KB_F10)) out[2] = 0x09;
++ else if (!strcmp(buf, KB_F11)) out[2] = 0x78;
++ else if (!strcmp(buf, KB_F12)) out[2] = 0x07;
++ else if (!strcmp(buf, KB_F14)) out[2] = 0x10;
++ else if (!strcmp(buf, KB_F15)) out[2] = 0x18;
++
++ else if (!strcmp(buf, KB_BACKTICK)) out[2] = 0x0e;
++ else if (!strcmp(buf, KB_1)) out[2] = 0x16;
++ else if (!strcmp(buf, KB_2)) out[2] = 0x1e;
++ else if (!strcmp(buf, KB_3)) out[2] = 0x26;
++ else if (!strcmp(buf, KB_4)) out[2] = 0x25;
++ else if (!strcmp(buf, KB_5)) out[2] = 0x2e;
++ else if (!strcmp(buf, KB_6)) out[2] = 0x36;
++ else if (!strcmp(buf, KB_7)) out[2] = 0x3d;
++ else if (!strcmp(buf, KB_8)) out[2] = 0x3e;
++ else if (!strcmp(buf, KB_9)) out[2] = 0x46;
++ else if (!strcmp(buf, KB_0)) out[2] = 0x45;
++ else if (!strcmp(buf, KB_HYPHEN)) out[2] = 0x4e;
++ else if (!strcmp(buf, KB_EQUALS)) out[2] = 0x55;
++ else if (!strcmp(buf, KB_BACKSPACE)) out[2] = 0x66;
++
++ else if (!strcmp(buf, KB_TAB)) out[2] = 0x0d;
++ else if (!strcmp(buf, KB_Q)) out[2] = 0x15;
++ else if (!strcmp(buf, KB_W)) out[2] = 0x1d;
++ else if (!strcmp(buf, KB_E)) out[2] = 0x24;
++ else if (!strcmp(buf, KB_R)) out[2] = 0x2d;
++ else if (!strcmp(buf, KB_T)) out[2] = 0x2d;
++ else if (!strcmp(buf, KB_Y)) out[2] = 0x35;
++ else if (!strcmp(buf, KB_U)) out[2] = 0x3c;
++ // else if (!strcmp(buf, "i\n")) out[2] = 0x1c;
++ else if (!strcmp(buf, KB_O)) out[2] = 0x44;
++ else if (!strcmp(buf, KB_P)) out[2] = 0x4d;
++ else if (!strcmp(buf, KB_LBRACKET)) out[2] = 0x54;
++ else if (!strcmp(buf, KB_RBRACKET)) out[2] = 0x5b;
++ else if (!strcmp(buf, KB_BACKSLASH)) out[2] = 0x5d;
++
++ else if (!strcmp(buf, KB_CAPS)) out[2] = 0x58;
++ else if (!strcmp(buf, KB_A)) out[2] = 0x1c;
++ else if (!strcmp(buf, KB_S)) out[2] = 0x1b;
++ else if (!strcmp(buf, KB_D)) out[2] = 0x23;
++ else if (!strcmp(buf, KB_F)) out[2] = 0x2b;
++ else if (!strcmp(buf, KB_G)) out[2] = 0x34;
++ else if (!strcmp(buf, KB_H)) out[2] = 0x33;
++ else if (!strcmp(buf, KB_J)) out[2] = 0x3b;
++ else if (!strcmp(buf, KB_K)) out[2] = 0x42;
++ else if (!strcmp(buf, KB_L)) out[2] = 0x4b;
++ else if (!strcmp(buf, KB_SEMI)) out[2] = 0x4c;
++ else if (!strcmp(buf, KB_QUOTE)) out[2] = 0x52;
++ else if (!strcmp(buf, KB_RET)) out[2] = 0x5a;
++
++ else if (!strcmp(buf, KB_LSHIFT)) out[2] = 0x88;
++ else if (!strcmp(buf, KB_Z)) out[2] = 0x1a;
++ else if (!strcmp(buf, KB_X)) out[2] = 0x22;
++ else if (!strcmp(buf, KB_C)) out[2] = 0x21;
++ else if (!strcmp(buf, KB_V)) out[2] = 0x2a;
++ else if (!strcmp(buf, KB_B)) out[2] = 0x32;
++ else if (!strcmp(buf, KB_N)) out[2] = 0x31;
++ else if (!strcmp(buf, KB_M)) out[2] = 0x3a;
++ else if (!strcmp(buf, KB_COMA)) out[2] = 0x41;
++ else if (!strcmp(buf, KB_PERIOD)) out[2] = 0x49;
++ // else if (!strcmp(buf, "/\n")) out[2] = 0x52; missing
++ else if (!strcmp(buf, KB_RSHIFT)) out[2] = 0x89;
++
++ else if (!strcmp(buf, KB_LCTL)) out[2] = 0x8c;
++ else if (!strcmp(buf, KB_META)) out[2] = 0x82;
++ else if (!strcmp(buf, KB_LALT)) out[2] = 0xba;
++ else if (!strcmp(buf, KB_SPACE)) out[2] = 0x29;
++ else if (!strcmp(buf, KB_RALT)) out[2] = 0x8b;
++ else if (!strcmp(buf, KB_MENU)) out[2] = 0x84;
++ else if (!strcmp(buf, KB_RCTL)) out[2] = 0x8d;
++
++ else if (!strcmp(buf, KB_PRNTSCN)) out[2] = 0xc3;
++ else if (!strcmp(buf, KB_SCRLCK)) out[2] = 0x7e;
++ else if (!strcmp(buf, KB_PAUSE)) out[2] = 0x91;
++ else if (!strcmp(buf, KB_INS)) out[2] = 0xc2;
++ else if (!strcmp(buf, KB_HOME)) out[2] = 0x94;
++ else if (!strcmp(buf, KB_PGUP)) out[2] = 0x96;
++ else if (!strcmp(buf, KB_DEL)) out[2] = 0xc0;
++ else if (!strcmp(buf, KB_END)) out[2] = 0x95;
++ else if (!strcmp(buf, KB_PGDWN)) out[2] = 0x97;
++
++ else if (!strcmp(buf, KB_UP_ARROW)) out[2] = 0x99;
++ else if (!strcmp(buf, KB_DOWN_ARROW)) out[2] = 0x98;
++ else if (!strcmp(buf, KB_LEFT_ARROW)) out[2] = 0x91;
++ else if (!strcmp(buf, KB_RIGHT_ARROW)) out[2] = 0x9b;
++
++ else if (!strcmp(buf, NUMPAD_LCK)) out[2] = 0x77;
++ else if (!strcmp(buf, NUMPAD_FWDSLASH)) out[2] = 0x90;
++ else if (!strcmp(buf, NUMPAD_STAR)) out[2] = 0x7c;
++ else if (!strcmp(buf, NUMPAD_HYPHEN)) out[2] = 0x7b;
++ else if (!strcmp(buf, NUMPAD_0)) out[2] = 0x70;
++ else if (!strcmp(buf, NUMPAD_1)) out[2] = 0x69;
++ else if (!strcmp(buf, NUMPAD_2)) out[2] = 0x72;
++ else if (!strcmp(buf, NUMPAD_3)) out[2] = 0x7a;
++ else if (!strcmp(buf, NUMPAD_4)) out[2] = 0x6b;
++ else if (!strcmp(buf, NUMPAD_5)) out[2] = 0x73;
++ else if (!strcmp(buf, NUMPAD_6)) out[2] = 0x74;
++ else if (!strcmp(buf, NUMPAD_7)) out[2] = 0x6c;
++ else if (!strcmp(buf, NUMPAD_8)) out[2] = 0x75;
++ else if (!strcmp(buf, NUMPAD_9)) out[2] = 0x7d;
++ else if (!strcmp(buf, NUMPAD_PLUS)) out[2] = 0x79;
++ else if (!strcmp(buf, NUMPAD_ENTER)) out[2] = 0x81;
++ else if (!strcmp(buf, NUMPAD_PERIOD)) out[2] = 0x71;
++ if (out[2])
++ goto success;
++
++ out[0] = 0x03;
++ if (!strcmp(buf, RAT_LCLICK)) out[4] = 0x01;
++ else if (!strcmp(buf, RAT_RCLICK)) out[4] = 0x02;
++ else if (!strcmp(buf, RAT_MCLICK)) out[4] = 0x03;
++ else if (!strcmp(buf, RAT_WHEEL_UP)) out[4] = 0x04;
++ else if (!strcmp(buf, RAT_WHEEL_DOWN)) out[4] = 0x05;
++ if (out[4] != 0)
++ goto success;
++
++ out[0] = 0x05;
++ if (!strcmp(buf, MEDIA_SCREENSHOT)) out[3] = 0x16;
++ else if (!strcmp(buf, MEDIA_SHOW_KEYBOARD)) out[3] = 0x19;
++ else if (!strcmp(buf, MEDIA_SHOW_DESKTOP)) out[3] = 0x1c;
++ else if (!strcmp(buf, MEDIA_START_RECORDING)) out[3] = 0x1e;
++ else if (!strcmp(buf, MEDIA_MIC_OFF)) out[3] = 0x01;
++ else if (!strcmp(buf, MEDIA_VOL_DOWN)) out[3] = 0x02;
++ else if (!strcmp(buf, MEDIA_VOL_UP)) out[3] = 0x03;
++ if (out[3])
++ goto success;
++
++ // restore bytes if invalid input
++ memcpy(out, save_buf, out_len);
++ kfree(save_buf);
++ return -EINVAL;
++
++success:
++ kfree(save_buf);
++ return 0;
++}
++
++const static char* __btn_map_to_string(struct device *raw_dev, enum btn_pair pair, enum btn_pair_side side, bool secondary)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ u8 *btn_block;
++ int offs;
++
++ // TODO: this little block is common
++ offs = side ? MAPPING_BLOCK_LEN / 2 : 0;
++ offs = secondary ? offs + BTN_CODE_LEN : offs;
++ btn_block = rog_ally->key_mapping[rog_ally->mode - 1][pair - 1] + offs;
++
++ if (btn_block[0] == 0x01) {
++ if (btn_block[1] == 0x01) return PAD_A;
++ if (btn_block[1] == 0x02) return PAD_B;
++ if (btn_block[1] == 0x03) return PAD_X;
++ if (btn_block[1] == 0x04) return PAD_Y;
++ if (btn_block[1] == 0x05) return PAD_LB;
++ if (btn_block[1] == 0x06) return PAD_RB;
++ if (btn_block[1] == 0x07) return PAD_LS;
++ if (btn_block[1] == 0x08) return PAD_RS;
++ if (btn_block[1] == 0x09) return PAD_DPAD_UP;
++ if (btn_block[1] == 0x0a) return PAD_DPAD_DOWN;
++ if (btn_block[1] == 0x0b) return PAD_DPAD_LEFT;
++ if (btn_block[1] == 0x0c) return PAD_DPAD_RIGHT;
++ if (btn_block[1] == 0x11) return PAD_VIEW;
++ if (btn_block[1] == 0x12) return PAD_MENU;
++ if (btn_block[1] == 0x13) return PAD_XBOX;
++ }
++
++ if (btn_block[0] == 0x02) {
++ if (btn_block[2] == 0x8f) return KB_M1;
++ if (btn_block[2] == 0x8e) return KB_M2;
++
++ if (btn_block[2] == 0x76) return KB_ESC;
++ if (btn_block[2] == 0x50) return KB_F1;
++ if (btn_block[2] == 0x60) return KB_F2;
++ if (btn_block[2] == 0x40) return KB_F3;
++ if (btn_block[2] == 0x0c) return KB_F4;
++ if (btn_block[2] == 0x03) return KB_F5;
++ if (btn_block[2] == 0x0b) return KB_F6;
++ if (btn_block[2] == 0x80) return KB_F7;
++ if (btn_block[2] == 0x0a) return KB_F8;
++ if (btn_block[2] == 0x01) return KB_F9;
++ if (btn_block[2] == 0x09) return KB_F10;
++ if (btn_block[2] == 0x78) return KB_F11;
++ if (btn_block[2] == 0x07) return KB_F12;
++ if (btn_block[2] == 0x10) return KB_F14;
++ if (btn_block[2] == 0x18) return KB_F15;
++
++ if (btn_block[2] == 0x0e) return KB_BACKTICK;
++ if (btn_block[2] == 0x16) return KB_1;
++ if (btn_block[2] == 0x1e) return KB_2;
++ if (btn_block[2] == 0x26) return KB_3;
++ if (btn_block[2] == 0x25) return KB_4;
++ if (btn_block[2] == 0x2e) return KB_5;
++ if (btn_block[2] == 0x36) return KB_6;
++ if (btn_block[2] == 0x3d) return KB_7;
++ if (btn_block[2] == 0x3e) return KB_8;
++ if (btn_block[2] == 0x46) return KB_9;
++ if (btn_block[2] == 0x45) return KB_0;
++ if (btn_block[2] == 0x4e) return KB_HYPHEN;
++ if (btn_block[2] == 0x55) return KB_EQUALS;
++ if (btn_block[2] == 0x66) return KB_BACKSPACE;
++
++ if (btn_block[2] == 0x0d) return KB_TAB;
++ if (btn_block[2] == 0x15) return KB_Q;
++ if (btn_block[2] == 0x1d) return KB_W;
++ if (btn_block[2] == 0x24) return KB_E;
++ if (btn_block[2] == 0x2d) return KB_R;
++ if (btn_block[2] == 0x2d) return KB_T;
++ if (btn_block[2] == 0x35) return KB_Y;
++ if (btn_block[2] == 0x3c) return KB_U;
++ // TODO: I
++ if (btn_block[2] == 0x44) return KB_O;
++ if (btn_block[2] == 0x4d) return KB_P;
++ if (btn_block[2] == 0x54) return KB_LBRACKET;
++ if (btn_block[2] == 0x5b) return KB_RBRACKET;
++ if (btn_block[2] == 0x5d) return KB_BACKSLASH;
++
++ if (btn_block[2] == 0x58) return KB_CAPS;
++ if (btn_block[2] == 0x1c) return KB_A;
++ if (btn_block[2] == 0x1b) return KB_S;
++ if (btn_block[2] == 0x23) return KB_D;
++ if (btn_block[2] == 0x2b) return KB_F;
++ if (btn_block[2] == 0x34) return KB_G;
++ if (btn_block[2] == 0x33) return KB_H;
++ if (btn_block[2] == 0x3b) return KB_J;
++ if (btn_block[2] == 0x42) return KB_K;
++ if (btn_block[2] == 0x4b) return KB_L;
++ if (btn_block[2] == 0x4c) return KB_SEMI;
++ if (btn_block[2] == 0x52) return KB_QUOTE;
++ if (btn_block[2] == 0x5a) return KB_RET;
++
++ if (btn_block[2] == 0x88) return KB_LSHIFT;
++ if (btn_block[2] == 0x1a) return KB_Z;
++ if (btn_block[2] == 0x22) return KB_X;
++ if (btn_block[2] == 0x21) return KB_C;
++ if (btn_block[2] == 0x2a) return KB_V;
++ if (btn_block[2] == 0x32) return KB_B;
++ if (btn_block[2] == 0x31) return KB_N;
++ if (btn_block[2] == 0x3a) return KB_M;
++ if (btn_block[2] == 0x41) return KB_COMA;
++ if (btn_block[2] == 0x49) return KB_PERIOD;
++ if (btn_block[2] == 0x89) return KB_RSHIFT;
++
++ if (btn_block[2] == 0x8c) return KB_LCTL;
++ if (btn_block[2] == 0x82) return KB_META;
++ if (btn_block[2] == 0xba) return KB_LALT;
++ if (btn_block[2] == 0x29) return KB_SPACE;
++ if (btn_block[2] == 0x8b) return KB_RALT;
++ if (btn_block[2] == 0x84) return KB_MENU;
++ if (btn_block[2] == 0x8d) return KB_RCTL;
++
++ if (btn_block[2] == 0xc3) return KB_PRNTSCN;
++ if (btn_block[2] == 0x7e) return KB_SCRLCK;
++ if (btn_block[2] == 0x91) return KB_PAUSE;
++ if (btn_block[2] == 0xc2) return KB_INS;
++ if (btn_block[2] == 0x94) return KB_HOME;
++ if (btn_block[2] == 0x96) return KB_PGUP;
++ if (btn_block[2] == 0xc0) return KB_DEL;
++ if (btn_block[2] == 0x95) return KB_END;
++ if (btn_block[2] == 0x97) return KB_PGDWN;
++
++ if (btn_block[2] == 0x99) return KB_UP_ARROW;
++ if (btn_block[2] == 0x98) return KB_DOWN_ARROW;
++ if (btn_block[2] == 0x91) return KB_LEFT_ARROW;
++ if (btn_block[2] == 0x9b) return KB_RIGHT_ARROW;
++
++ if (btn_block[2] == 0x77) return NUMPAD_LCK;
++ if (btn_block[2] == 0x90) return NUMPAD_FWDSLASH;
++ if (btn_block[2] == 0x7c) return NUMPAD_STAR;
++ if (btn_block[2] == 0x7b) return NUMPAD_HYPHEN;
++ if (btn_block[2] == 0x70) return NUMPAD_0;
++ if (btn_block[2] == 0x69) return NUMPAD_1;
++ if (btn_block[2] == 0x72) return NUMPAD_2;
++ if (btn_block[2] == 0x7a) return NUMPAD_3;
++ if (btn_block[2] == 0x6b) return NUMPAD_4;
++ if (btn_block[2] == 0x73) return NUMPAD_5;
++ if (btn_block[2] == 0x74) return NUMPAD_6;
++ if (btn_block[2] == 0x6c) return NUMPAD_7;
++ if (btn_block[2] == 0x75) return NUMPAD_8;
++ if (btn_block[2] == 0x7d) return NUMPAD_9;
++ if (btn_block[2] == 0x79) return NUMPAD_PLUS;
++ if (btn_block[2] == 0x81) return NUMPAD_ENTER;
++ if (btn_block[2] == 0x71) return NUMPAD_PERIOD;
++ }
++
++ if (btn_block[0] == 0x03) {
++ if (btn_block[4] == 0x01) return RAT_LCLICK;
++ if (btn_block[4] == 0x02) return RAT_RCLICK;
++ if (btn_block[4] == 0x03) return RAT_MCLICK;
++ if (btn_block[4] == 0x04) return RAT_WHEEL_UP;
++ if (btn_block[4] == 0x05) return RAT_WHEEL_DOWN;
++ }
++
++ if (btn_block[0] == 0x05) {
++ if (btn_block[3] == 0x16) return MEDIA_SCREENSHOT;
++ if (btn_block[3] == 0x19) return MEDIA_SHOW_KEYBOARD;
++ if (btn_block[3] == 0x1c) return MEDIA_SHOW_DESKTOP;
++ if (btn_block[3] == 0x1e) return MEDIA_START_RECORDING;
++ if (btn_block[3] == 0x01) return MEDIA_MIC_OFF;
++ if (btn_block[3] == 0x02) return MEDIA_VOL_DOWN;
++ if (btn_block[3] == 0x03) return MEDIA_VOL_UP;
++ }
++
++ 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)
++{
++ u8 *hidbuf;
++ int ret;
++
++ hidbuf = kzalloc(FEATURE_ROG_ALLY_REPORT_SIZE, GFP_KERNEL);
++ if (!hidbuf)
++ return -ENOMEM;
++
++ hidbuf[0] = FEATURE_KBD_REPORT_ID;
++ hidbuf[1] = 0xD1;
++ hidbuf[2] = ally_xpad_cmd_check_ready;
++ hidbuf[3] = 01;
++ ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
++ if (ret < 0)
++ goto report_fail;
++
++ hidbuf[0] = hidbuf[1] = hidbuf[2] = hidbuf[3] = 0;
++ ret = asus_kbd_get_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
++ if (ret < 0)
++ goto report_fail;
++
++ ret = hidbuf[2] == ally_xpad_cmd_check_ready;
++ if (!ret) {
++ hid_warn(hdev, "ROG Ally not ready\n");
++ ret = -ENOMSG;
++ }
++
++ kfree(hidbuf);
++ return ret;
++
++report_fail:
++ hid_dbg(hdev, "ROG Ally check failed get report: %d\n", ret);
++ kfree(hidbuf);
++ return ret;
++}
++
++/********** BUTTON REMAPPING *********************************************************************/
++static void __btn_pair_to_pkt(struct device *raw_dev, enum btn_pair pair, u8 *out, int out_len)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++
++ out[0] = FEATURE_KBD_REPORT_ID;
++ out[1] = 0xD1;
++ out[2] = 0x02;
++ out[3] = pair;
++ out[4] = 0x2c; //length
++ memcpy(&out[5], &rog_ally->key_mapping[rog_ally->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 device *raw_dev, const char *buf, enum btn_pair pair, int side,
++ bool secondary)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ u8 *key_code;
++ int offs;
++
++ offs = side ? MAPPING_BLOCK_LEN / 2 : 0;
++ offs = secondary ? offs + BTN_CODE_LEN : offs;
++ key_code = rog_ally->key_mapping[rog_ally->mode - 1][pair - 1] + offs;
++
++ return __string_to_key_code(buf, key_code, BTN_CODE_LEN);
++}
++
++/* Apply the mapping pair to the device */
++static int __gamepad_set_mapping(struct device *raw_dev, enum btn_pair pair)
++{
++ struct hid_device *hdev = to_hid_device(raw_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;
++
++ __btn_pair_to_pkt(raw_dev, pair, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
++ ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
++ kfree(hidbuf);
++
++ return ret;
++}
++
++static int __gamepad_set_mapping_all(struct device *raw_dev)
++{
++ struct hid_device *hdev = to_hid_device(raw_dev);
++ int ret;
++
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_dpad_u_d);
++ if (ret < 0)
++ return ret;
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_dpad_l_r);
++ if (ret < 0)
++ return ret;
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_ls_rs);
++ if (ret < 0)
++ return ret;
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_lb_rb);
++ if (ret < 0)
++ return ret;
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_a_b);
++ if (ret < 0)
++ return ret;
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_x_y);
++ if (ret < 0)
++ return ret;
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_view_menu);
++ if (ret < 0)
++ return ret;
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_m1_m2);
++ if (ret < 0)
++ return ret;
++ return __gamepad_set_mapping(&hdev->dev, btn_pair_lt_rt);
++}
++
++static ssize_t btn_mapping_apply_store(struct device *raw_dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ int ret = __gamepad_set_mapping_all(raw_dev);
++ if (ret < 0)
++ return ret;
++ return count;
++}
++ALLY_DEVICE_ATTR_WO(btn_mapping_apply, apply);
++
++/* button map attributes, regular and macro*/
++#define ALLY_BTN_SHOW(_fname, _pair, _side, _secondary) \
++static ssize_t _fname##_show(struct device *raw_dev, struct device_attribute *attr, char *buf) { \
++ return sysfs_emit(buf, "%s\n", __btn_map_to_string(raw_dev, _pair, _side, _secondary)); \
++}
++
++#define ALLY_BTN_STORE(_fname, _pair, _side, _secondary) \
++static ssize_t _fname##_store(struct device *raw_dev, struct device_attribute *attr, \
++ const char *buf, size_t count) { \
++ int ret = __gamepad_mapping_store(raw_dev, buf, _pair, _side, _secondary); \
++ if (ret < 0) return ret; \
++ return count; \
++}
++
++#define ALLY_BTN_MAPPING(_fname, _sysname, _pair, _side) \
++ ALLY_BTN_SHOW(_fname, _pair, _side, false); \
++ ALLY_BTN_STORE(_fname, _pair, _side, false); \
++ ALLY_BTN_SHOW(_fname##_macro, _pair, _side, true); \
++ ALLY_BTN_STORE(_fname##_macro, _pair, _side, true); \
++ ALLY_DEVICE_ATTR_RW(_fname, _sysname); \
++ ALLY_DEVICE_ATTR_RW(_fname##_macro, _sysname##_macro);
++
++ALLY_BTN_MAPPING(btn_mapping_m2, M2, btn_pair_m1_m2, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_m1, M1, btn_pair_m1_m2, btn_pair_side_right);
++ALLY_BTN_MAPPING(btn_mapping_a, A, btn_pair_a_b, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_b, B, btn_pair_a_b, btn_pair_side_right);
++ALLY_BTN_MAPPING(btn_mapping_x, X, btn_pair_x_y, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_y, Y, btn_pair_x_y, btn_pair_side_right);
++ALLY_BTN_MAPPING(btn_mapping_lb, LB, btn_pair_lb_rb, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_rb, RB, btn_pair_lb_rb, btn_pair_side_right);
++ALLY_BTN_MAPPING(btn_mapping_ls, LS, btn_pair_ls_rs, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_rs, RS, btn_pair_ls_rs, btn_pair_side_right);
++ALLY_BTN_MAPPING(btn_mapping_lt, LT, btn_pair_lt_rt, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_rt, RT, btn_pair_lt_rt, btn_pair_side_right);
++ALLY_BTN_MAPPING(btn_mapping_dpad_u, dpad_U, btn_pair_dpad_u_d, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_dpad_d, dpad_D, btn_pair_dpad_u_d, btn_pair_side_right);
++ALLY_BTN_MAPPING(btn_mapping_dpad_l, dpad_L, btn_pair_dpad_l_r, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_dpad_r, dpad_R, btn_pair_dpad_l_r, btn_pair_side_right);
++ALLY_BTN_MAPPING(btn_mapping_view, view, btn_pair_view_menu, btn_pair_side_left);
++ALLY_BTN_MAPPING(btn_mapping_menu, menu, btn_pair_view_menu, btn_pair_side_right);
++
++static void __gamepad_mapping_xpad_default(struct asus_rog_ally *rog_ally)
++{
++ u8 map1[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};
++ u8 map2[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};
++ u8 map3[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};
++ u8 map4[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};
++ u8 map5[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};
++ u8 map6[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};
++ u8 map7[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};
++ u8 map8[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};
++ u8 map9[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};
++ memcpy(&rog_ally->key_mapping[0][0], &map1, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[0][1], &map2, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[0][2], &map3, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[0][3], &map4, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[0][4], &map5, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[0][5], &map6, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[0][6], &map7, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[0][7], &map8, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[0][8], &map9, MAPPING_BLOCK_LEN);
++}
++
++static void __gamepad_mapping_wasd_default(struct asus_rog_ally *rog_ally)
++{
++ u8 map1[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};
++ u8 map2[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};
++ u8 map3[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};
++ u8 map4[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};
++ u8 map5[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};
++ u8 map6[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};
++ u8 map7[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};
++ u8 map8[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};
++ u8 map9[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};
++ memcpy(&rog_ally->key_mapping[1][0], &map1, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[1][1], &map2, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[1][2], &map3, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[1][3], &map4, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[1][4], &map5, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[1][5], &map6, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[1][6], &map7, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[1][7], &map8, MAPPING_BLOCK_LEN);
++ memcpy(&rog_ally->key_mapping[1][8], &map9, MAPPING_BLOCK_LEN);
++}
++
++static ssize_t btn_mapping_reset_store(struct device *raw_dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ switch (rog_ally->mode)
++ {
++ case ally_xpad_mode_game:
++ __gamepad_mapping_xpad_default(rog_ally);
++ break;
++ case ally_xpad_mode_wasd:
++ __gamepad_mapping_wasd_default(rog_ally);
++ break;
++ default:
++ __gamepad_mapping_xpad_default(rog_ally);
++ break;
++ }
++
++ return count;
++}
++
++ALLY_DEVICE_ATTR_WO(btn_mapping_reset, reset);
++
++static struct attribute *gamepad_button_mapping_attrs[] = { &dev_attr_btn_mapping_apply.attr,
++ &dev_attr_btn_mapping_reset.attr,
++ &dev_attr_btn_mapping_m2.attr,
++ &dev_attr_btn_mapping_m2_macro.attr,
++ &dev_attr_btn_mapping_m1.attr,
++ &dev_attr_btn_mapping_m1_macro.attr,
++ &dev_attr_btn_mapping_a.attr,
++ &dev_attr_btn_mapping_a_macro.attr,
++ &dev_attr_btn_mapping_b.attr,
++ &dev_attr_btn_mapping_b_macro.attr,
++ &dev_attr_btn_mapping_x.attr,
++ &dev_attr_btn_mapping_x_macro.attr,
++ &dev_attr_btn_mapping_y.attr,
++ &dev_attr_btn_mapping_y_macro.attr,
++ &dev_attr_btn_mapping_lb.attr,
++ &dev_attr_btn_mapping_lb_macro.attr,
++ &dev_attr_btn_mapping_rb.attr,
++ &dev_attr_btn_mapping_rb_macro.attr,
++ &dev_attr_btn_mapping_ls.attr,
++ &dev_attr_btn_mapping_ls_macro.attr,
++ &dev_attr_btn_mapping_rs.attr,
++ &dev_attr_btn_mapping_rs_macro.attr,
++ &dev_attr_btn_mapping_lt.attr,
++ &dev_attr_btn_mapping_lt_macro.attr,
++ &dev_attr_btn_mapping_rt.attr,
++ &dev_attr_btn_mapping_rt_macro.attr,
++ &dev_attr_btn_mapping_dpad_u.attr,
++ &dev_attr_btn_mapping_dpad_u_macro.attr,
++ &dev_attr_btn_mapping_dpad_d.attr,
++ &dev_attr_btn_mapping_dpad_d_macro.attr,
++ &dev_attr_btn_mapping_dpad_l.attr,
++ &dev_attr_btn_mapping_dpad_l_macro.attr,
++ &dev_attr_btn_mapping_dpad_r.attr,
++ &dev_attr_btn_mapping_dpad_r_macro.attr,
++ &dev_attr_btn_mapping_view.attr,
++ &dev_attr_btn_mapping_view_macro.attr,
++ &dev_attr_btn_mapping_menu.attr,
++ &dev_attr_btn_mapping_menu_macro.attr,
++ NULL };
++
++static const struct attribute_group ally_controller_button_mapping_attr_group = {
++ .name = "button_mapping",
++ .attrs = gamepad_button_mapping_attrs,
++};
++
++/********** GAMEPAD MODE *************************************************************************/
++// TODO: general purpose request function which checks the device is ready before setting
++/* The gamepad mode also needs to be set on boot/mod-load and shutdown */
++static ssize_t __gamepad_set_mode(struct device *raw_dev, int val)
++{
++ struct hid_device *hdev = to_hid_device(raw_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;
++
++ hidbuf[0] = FEATURE_KBD_REPORT_ID;
++ hidbuf[1] = 0xD1;
++ hidbuf[2] = ally_xpad_cmd_set_mode;
++ hidbuf[3] = 0x01;
++ hidbuf[4] = val;
++
++ ret = __gamepad_check_ready(hdev);
++ if (ret < 0)
++ goto report_fail;
++
++ ret = asus_kbd_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_mode_show(struct device *raw_dev, struct device_attribute *attr, char *buf)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ return sysfs_emit(buf, "%d\n", rog_ally->mode);
++}
++
++static ssize_t gamepad_mode_store(struct device *raw_dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ int ret, val;
++
++ ret = kstrtoint(buf, 0, &val);
++ if (ret)
++ return ret;
++
++ if (val < ally_xpad_mode_game || val > ally_xpad_mode_mouse)
++ return -EINVAL;
++
++ rog_ally->mode = val;
++
++ ret = __gamepad_set_mode(raw_dev, val);
++ if (ret < 0)
++ return ret;
++
++ return count;
++}
++
++DEVICE_ATTR_RW(gamepad_mode);
++
++static struct attribute *gamepad_device_attrs[] = { &dev_attr_gamepad_mode.attr, NULL };
++
++static const struct attribute_group ally_controller_attr_group = {
++ .attrs = gamepad_device_attrs,
++};
++
++/********** ANALOGUE DEADZONES ********************************************************************/
++static ssize_t __gamepad_set_deadzones(struct device *raw_dev, enum ally_xpad_axis axis,
++ const char *buf)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ struct hid_device *hdev = to_hid_device(raw_dev);
++ int ret, cmd, side, is_tr;
++ u32 inner, outer;
++ u8 *hidbuf;
++
++ if (sscanf(buf, "%d %d", &inner, &outer) != 2)
++ return -EINVAL;
++
++ if (inner > 64 || outer > 64 || inner > outer)
++ return -EINVAL;
++
++ is_tr = axis > ally_xpad_axis_xy_right;
++ side = axis == ally_xpad_axis_xy_right || axis == ally_xpad_axis_z_right ? 2 : 0;
++ cmd = is_tr ? ally_xpad_cmd_set_js_dz : ally_xpad_cmd_set_tr_dz;
++
++ rog_ally->deadzones[is_tr][side] = inner;
++ rog_ally->deadzones[is_tr][side + 1] = outer;
++
++ 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_KBD_REPORT_ID;
++ hidbuf[1] = 0xD1;
++ hidbuf[2] = cmd;
++ hidbuf[3] = 0x04; // length
++ hidbuf[4] = rog_ally->deadzones[is_tr][0];
++ hidbuf[5] = rog_ally->deadzones[is_tr][1];
++ hidbuf[6] = rog_ally->deadzones[is_tr][2];
++ hidbuf[7] = rog_ally->deadzones[is_tr][3];
++
++ ret = asus_kbd_set_report(hdev, hidbuf, FEATURE_ROG_ALLY_REPORT_SIZE);
++ kfree(hidbuf);
++ return ret;
++}
++
++static ssize_t axis_xyz_deadzone_index_show(struct device *raw_dev, struct device_attribute *attr,
++ char *buf)
++{
++ return sysfs_emit(buf, "inner outer\n");
++}
++
++static ssize_t axis_xy_left_deadzone_show(struct device *raw_dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ return sysfs_emit(buf, "%d %d\n", rog_ally->deadzones[0][0], rog_ally->deadzones[0][1]);
++}
++
++static ssize_t axis_xy_left_deadzone_store(struct device *raw_dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ int ret = __gamepad_set_deadzones(raw_dev, ally_xpad_axis_xy_left, buf);
++ if (ret < 0)
++ return ret;
++ return count;
++}
++
++static ssize_t axis_xy_right_deadzone_show(struct device *raw_dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ return sysfs_emit(buf, "%d %d\n", rog_ally->deadzones[0][2], rog_ally->deadzones[0][3]);
++}
++
++static ssize_t axis_xy_right_deadzone_store(struct device *raw_dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ int ret = __gamepad_set_deadzones(raw_dev, ally_xpad_axis_xy_right, buf);
++ if (ret < 0)
++ return ret;
++ return count;
++}
++
++static ssize_t axis_z_left_deadzone_show(struct device *raw_dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ return sysfs_emit(buf, "%d %d\n", rog_ally->deadzones[1][0], rog_ally->deadzones[1][1]);
++}
++
++static ssize_t axis_z_left_deadzone_store(struct device *raw_dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ int ret = __gamepad_set_deadzones(raw_dev, ally_xpad_axis_z_left, buf);
++ if (ret < 0)
++ return ret;
++ return count;
++}
++
++static ssize_t axis_z_right_deadzone_show(struct device *raw_dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct asus_rog_ally *rog_ally = __rog_ally_data(raw_dev);
++ return sysfs_emit(buf, "%d %d\n", rog_ally->deadzones[1][2], rog_ally->deadzones[1][3]);
++}
++
++static ssize_t axis_z_right_deadzone_store(struct device *raw_dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ int ret = __gamepad_set_deadzones(raw_dev, ally_xpad_axis_z_right, buf);
++ if (ret < 0)
++ return ret;
++ return count;
++}
++
++ALLY_DEVICE_ATTR_RO(axis_xyz_deadzone_index, deadzone_index);
++ALLY_DEVICE_ATTR_RW(axis_xy_left_deadzone, deadzone);
++ALLY_DEVICE_ATTR_RW(axis_xy_right_deadzone, deadzone);
++ALLY_DEVICE_ATTR_RW(axis_z_left_deadzone, deadzone);
++ALLY_DEVICE_ATTR_RW(axis_z_right_deadzone, deadzone);
++
++static struct attribute *gamepad_axis_xy_left_attrs[] = { &dev_attr_axis_xy_left_deadzone.attr,
++ &dev_attr_axis_xyz_deadzone_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_axis_xy_right_deadzone.attr,
++ &dev_attr_axis_xyz_deadzone_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_axis_z_left_deadzone.attr,
++ &dev_attr_axis_xyz_deadzone_index.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_axis_z_right_deadzone.attr,
++ &dev_attr_axis_xyz_deadzone_index.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,
++ &ally_controller_button_mapping_attr_group,
++ NULL
++};
++
++static int asus_rog_ally_probe(struct hid_device *hdev, const struct rog_ops *ops)
++{
++ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
++ int ret;
++
++ /* all ROG devices have this HID interface but we will focus on Ally for now */
++ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && hid_is_usb(hdev)) {
++ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
++
++ if (intf->altsetting->desc.bInterfaceNumber == 0) {
++ hid_info(hdev, "Setting up ROG USB interface\n");
++ /* initialise and set up USB, common to ROG */
++ // TODO:
++
++ /* initialise the Ally data */
++ if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
++ hid_info(hdev, "Setting up ROG Ally interface\n");
++
++ drvdata->rog_ally_data = devm_kzalloc(
++ &hdev->dev, sizeof(*drvdata->rog_ally_data), GFP_KERNEL);
++ if (!drvdata->rog_ally_data) {
++ hid_err(hdev, "Can't alloc Asus ROG USB interface\n");
++ ret = -ENOMEM;
++ goto err_stop_hw;
++ }
++ drvdata->rog_ally_data->mode = ally_xpad_mode_game;
++ drvdata->rog_ally_data->deadzones[0][1] = 64;
++ drvdata->rog_ally_data->deadzones[0][3] = 64;
++ drvdata->rog_ally_data->deadzones[1][1] = 64;
++ drvdata->rog_ally_data->deadzones[1][3] = 64;
++
++ ret = __gamepad_set_mode(&hdev->dev, ally_xpad_mode_game);
++ if (ret < 0)
++ return ret;
++ __gamepad_mapping_xpad_default(drvdata->rog_ally_data);
++ // these calls will never error so ignore the return
++ __gamepad_mapping_store(&hdev->dev, "kb_f14", btn_pair_m1_m2,
++ btn_pair_side_left, false); // M2
++ __gamepad_mapping_store(&hdev->dev, "kb_f15", btn_pair_m1_m2,
++ btn_pair_side_right, false); // M1
++ ret = __gamepad_set_mapping(&hdev->dev, btn_pair_m1_m2);
++ if (ret < 0)
++ return ret;
++ }
++
++ if (sysfs_create_groups(&hdev->dev.kobj, gamepad_device_attr_groups))
++ goto err_stop_hw;
++ }
++ }
++
++ return 0;
++err_stop_hw:
++ hid_hw_stop(hdev);
++ return ret;
++}
++
++void asus_rog_ally_remove(struct hid_device *hdev, const struct rog_ops *ops)
++{
++ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
++ if (drvdata->rog_ally_data) {
++ __gamepad_set_mode(&hdev->dev, ally_xpad_mode_mouse);
++ sysfs_remove_groups(&hdev->dev.kobj, gamepad_device_attr_groups);
++ }
++}
++
++const struct rog_ops rog_ally = {
++ .probe = asus_rog_ally_probe,
++ .remove = asus_rog_ally_remove,
++};
+diff --git a/drivers/hid/hid-asus.h b/drivers/hid/hid-asus.h
+new file mode 100644
+index 000000000000..e7d7041b56fb
+--- /dev/null
++++ b/drivers/hid/hid-asus.h
+@@ -0,0 +1,51 @@
++#include <linux/hid.h>
++#include <linux/types.h>
++
++#define FEATURE_KBD_REPORT_ID 0x5a
++#define FEATURE_KBD_REPORT_SIZE 16
++#define FEATURE_KBD_LED_REPORT_ID1 0x5d
++#define FEATURE_KBD_LED_REPORT_ID2 0x5e
++#define FEATURE_ROG_ALLY_REPORT_SIZE 64
++
++#define QUIRK_FIX_NOTEBOOK_REPORT BIT(0)
++#define QUIRK_NO_INIT_REPORTS BIT(1)
++#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
++#define QUIRK_IS_MULTITOUCH BIT(3)
++#define QUIRK_NO_CONSUMER_USAGES BIT(4)
++#define QUIRK_USE_KBD_BACKLIGHT BIT(5)
++#define QUIRK_T100_KEYBOARD BIT(6)
++#define QUIRK_T100CHI BIT(7)
++#define QUIRK_G752_KEYBOARD BIT(8)
++#define QUIRK_T90CHI BIT(9)
++#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)
++
++struct asus_drvdata {
++ unsigned long quirks;
++ struct hid_device *hdev;
++ struct input_dev *input;
++ struct input_dev *tp_kbd_input;
++ struct asus_kbd_leds *kbd_backlight;
++ const struct asus_touchpad_info *tp;
++ bool enable_backlight;
++ struct power_supply *battery;
++ struct power_supply_desc battery_desc;
++ int battery_capacity;
++ int battery_stat;
++ bool battery_in_query;
++ unsigned long battery_next_query;
++ struct asus_rog_ally *rog_ally_data;
++};
++
++extern int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size);
++
++extern int asus_kbd_get_report(struct hid_device *hdev, u8 *out_buf, size_t out_buf_size);
++
++struct rog_ops {
++ int (*probe) (struct hid_device *hdev, const struct rog_ops *ops);
++ void (*remove) (struct hid_device *hdev, const struct rog_ops *ops);
++};
++
++extern const struct rog_ops rog_ally;
+\ No newline at end of file
+--
+2.41.0
+
diff --git a/SOURCES/v2-0001-platform-x86-asus-wmi-disable-USB0-hub-on-ROG-All.patch b/SOURCES/v2-0001-platform-x86-asus-wmi-disable-USB0-hub-on-ROG-All.patch
new file mode 100644
index 0000000..3c89787
--- /dev/null
+++ b/SOURCES/v2-0001-platform-x86-asus-wmi-disable-USB0-hub-on-ROG-All.patch
@@ -0,0 +1,113 @@
+From 0a399a37fbe6f6b2b9062e1c076df28608b628c9 Mon Sep 17 00:00:00 2001
+From: "Luke D. Jones" <luke@ljones.dev>
+Date: Fri, 24 Nov 2023 21:13:11 +1300
+Subject: [PATCH v2] platform/x86: asus-wmi: disable USB0 hub on ROG Ally
+ before suspend
+
+ASUS have worked around an issue in XInput where it doesn't support USB
+selective suspend, whcih causes suspend issues in Windows. They worked
+around this by adjusting the MCU firmware to disable the USB0 hub when
+the screen is switched off during the Microsoft DSM suspend path in ACPI.
+
+The issue we have with this however is one of timing - the call the tells
+the MCU to this isn't able to complete before suspend is done so we call
+this in a prepare() and add a small msleep() to ensure it is done. This
+must be done before the screen is switched off to prevent a variety of
+possible races.
+
+Without this the MCU is unable to initialise itself correctly on resume.
+
+Signed-off-by: Luke D. Jones <luke@ljones.dev>
+---
+ drivers/platform/x86/asus-wmi.c | 40 +++++++++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
+index 6a79f16233ab..563c9ab31bc7 100644
+--- a/drivers/platform/x86/asus-wmi.c
++++ b/drivers/platform/x86/asus-wmi.c
+@@ -16,6 +16,7 @@
+ #include <linux/acpi.h>
+ #include <linux/backlight.h>
+ #include <linux/debugfs.h>
++#include <linux/delay.h>
+ #include <linux/dmi.h>
+ #include <linux/fb.h>
+ #include <linux/hwmon.h>
+@@ -132,6 +133,9 @@ module_param(fnlock_default, bool, 0444);
+ #define ASUS_SCREENPAD_BRIGHT_MAX 255
+ #define ASUS_SCREENPAD_BRIGHT_DEFAULT 60
+
++/* Controls the power state of the USB0 hub on ROG Ally which input is on */
++#define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE"
++
+ static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
+
+ static int throttle_thermal_policy_write(struct asus_wmi *);
+@@ -300,6 +304,9 @@ struct asus_wmi {
+
+ bool fnlock_locked;
+
++ /* The ROG Ally device requires the USB hub to be disabled before suspend */
++ bool pre_suspend_ec0_csee_disable;
++
+ struct asus_wmi_debug debug;
+
+ struct asus_wmi_driver *driver;
+@@ -4488,6 +4495,8 @@ static int asus_wmi_add(struct platform_device *pdev)
+ asus->nv_temp_tgt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_THERM_TARGET);
+ asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD);
+ asus->mini_led_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
++ asus->pre_suspend_ec0_csee_disable = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE)
++ && dmi_match(DMI_BOARD_NAME, "RC71L");
+
+ err = fan_boost_mode_check_present(asus);
+ if (err)
+@@ -4654,6 +4663,35 @@ static int asus_hotk_resume(struct device *device)
+ asus_wmi_fnlock_update(asus);
+
+ asus_wmi_tablet_mode_get_state(asus);
++
++ return 0;
++}
++
++static int asus_hotk_resume_early(struct device *device)
++{
++ struct asus_wmi *asus = dev_get_drvdata(device);
++
++ if (asus->pre_suspend_ec0_csee_disable) {
++ /* sleep required to ensure USB0 is enabled before drivers notice */
++ if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB8)))
++ pr_warn("ASUS ROG Ally failed to set USB hub power on\n");
++ }
++
++ return 0;
++}
++
++static int asus_hotk_prepare(struct device *device)
++{
++ struct asus_wmi *asus = dev_get_drvdata(device);
++
++ if (asus->pre_suspend_ec0_csee_disable) {
++ /* sleep required to ensure USB0 is disabled before sleep continues */
++ if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB7)))
++ pr_warn("ASUS ROG Ally failed to set USB hub power off\n");
++ else
++ msleep(100);
++ }
++
+ return 0;
+ }
+
+@@ -4701,6 +4739,8 @@ static const struct dev_pm_ops asus_pm_ops = {
+ .thaw = asus_hotk_thaw,
+ .restore = asus_hotk_restore,
+ .resume = asus_hotk_resume,
++ .resume_early = asus_hotk_resume_early,
++ .prepare = asus_hotk_prepare,
+ };
+
+ /* Registration ***************************************************************/
+--
+2.43.0
+
diff --git a/SPECS/kernel.spec b/SPECS/kernel.spec
index 64821ef..14dedaa 100644
--- a/SPECS/kernel.spec
+++ b/SPECS/kernel.spec
@@ -160,18 +160,18 @@ Summary: The Linux kernel
# the --with-release option overrides this setting.)
%define debugbuildsenabled 1
%define buildid .fsync
-%define specrpmversion 6.6.2
-%define specversion 6.6.2
+%define specrpmversion 6.6.3
+%define specversion 6.6.3
%define patchversion 6.6
-%define pkgrelease 201
+%define pkgrelease 200
%define kversion 6
-%define tarfile_release 6.6.2
+%define tarfile_release 6.6.3
# This is needed to do merge window version magic
%define patchlevel 6
# This allows pkg_release to have configurable %%{?dist} tag
-%define specrelease 202%{?buildid}%{?dist}
+%define specrelease 201%{?buildid}%{?dist}
# This defines the kabi tarball version
-%define kabiversion 6.6.2
+%define kabiversion 6.6.3
# If this variable is set to 1, a bpf selftests build failure will cause a
# fatal kernel package build error
@@ -984,7 +984,6 @@ Patch200: tkg.patch
Patch202: fsync.patch
Patch203: OpenRGB.patch
Patch206: amdgpu-si-cik-default.patch
-Patch207: acso.patch
Patch208: winesync.patch
# device specific patches
@@ -992,23 +991,43 @@ Patch300: linux-surface.patch
Patch301: steam-deck.patch
Patch302: asus-linux.patch
Patch303: lenovo-legion-laptop.patch
-Patch306: rog-ally-side-keys-fix.patch
-Patch308: rog-ally-alsa.patch
+# workaround for i915 getting stuck during async page flips on Nvidia PRIME systems
+Patch308: 0001-drm-i915-quirks-disable-async-flipping-on-specific-d.patch
+Patch309: 0002-drm-i915-add-kernel-parameter-to-disable-async-page-.patch
+# ROG Ally
+Patch315: rog-ally-audio-fix.patch
+Patch316: 0001-hid-asus-nero-patches-rogue.patch
+Patch317: ROG-ALLY-NCT6775-PLATFORM.patch
+Patch318: v10-0001-HID-asus-fix-more-n-key-report-descriptors-if-n-.patch
+Patch319: v10-0002-HID-asus-make-asus_kbd_init-generic-remove-rog_n.patch
+Patch320: v10-0003-HID-asus-add-ROG-Ally-N-Key-ID-and-keycodes.patch
+Patch321: v10-0004-HID-asus-add-ROG-Ally-xpad-settings.patch
+Patch322: 0001-add-acpi_call.patch
+Patch323: rog-ally-bmc150.patch
+Patch324: uinput.patch
+Patch325: v2-0001-platform-x86-asus-wmi-disable-USB0-hub-on-ROG-All.patch
# hdr: https://github.com/CachyOS/kernel-patches
Patch407: 0001-amd-hdr.patch
+# steamdeck oled patches
+Patch310: steamdeck-oled-wifi.patch
+Patch311: steamdeck-oled-bt.patch
+Patch312: steamdeck-oled-audio.patch
+Patch314: steamdeck-oled-hw-quirks.patch
+
# temporary patches
-Patch401: 0001-Revert-PCI-Add-a-REBAR-size-quirk-for-Sapphire-RX-56.patch
+Patch401: 0001-Remove-REBAR-size-quirk-for-Sapphire-RX-5600-XT-Puls.patch
Patch405: mt76:-mt7921:-Disable-powersave-features-by-default.patch
Patch408: 0001-acpi-proc-idle-skip-dummy-wait.patch
-# async flipping on PRIME systems can introduce problems
-Patch409: 0001-drm-i915-quirks-disable-async-flipping-on-specific-d.patch
-Patch410: 0002-drm-i915-add-kernel-parameter-to-disable-async-page-.patch
# Fixes the steam deck not coming back from hibernation
Patch411: 0001-Revert-nvme-pci-drop-redundant-pci_enable_pcie_error.patch
# Allows corectl to work out of the box
Patch412: 0001-Set-amdgpu.ppfeaturemask-0xffffffff-as-default.patch
+# steam deck: https://gitlab.com/evlaV/linux-integration/-/commit/d6935e6d62b35ec14d2187c2cfdc19934d66db6d
+Patch415: 0001-NOT-FOR-UPSTREAM-PM-suspend-Disable-s2idle-on-Steam-.patch
+# amdgpu bug fix: https://gitlab.freedesktop.org/drm/amd/-/issues/2733
+Patch416: amdgpu-bug-fix.patch
# Allow to set custom USB pollrate for specific devices like so:
# usbcore.interrupt_interval_override=045e:00db:16,1bcf:0005:1
@@ -1772,7 +1791,6 @@ ApplyOptionalPatch tkg.patch
ApplyOptionalPatch fsync.patch
ApplyOptionalPatch OpenRGB.patch
ApplyOptionalPatch amdgpu-si-cik-default.patch
-ApplyOptionalPatch acso.patch
ApplyOptionalPatch winesync.patch
# device specific patches
@@ -1780,23 +1798,43 @@ ApplyOptionalPatch linux-surface.patch
ApplyOptionalPatch steam-deck.patch
ApplyOptionalPatch asus-linux.patch
ApplyOptionalPatch lenovo-legion-laptop.patch
-ApplyOptionalPatch rog-ally-side-keys-fix.patch
-ApplyOptionalPatch rog-ally-alsa.patch
+# workaround for i915 getting stuck during async page flips on Nvidia PRIME systems
+ApplyOptionalPatch 0001-drm-i915-quirks-disable-async-flipping-on-specific-d.patch
+ApplyOptionalPatch 0002-drm-i915-add-kernel-parameter-to-disable-async-page-.patch
+# ROG Ally
+ApplyOptionalPatch rog-ally-audio-fix.patch
+ApplyOptionalPatch 0001-hid-asus-nero-patches-rogue.patch
+ApplyOptionalPatch ROG-ALLY-NCT6775-PLATFORM.patch
+ApplyOptionalPatch v10-0001-HID-asus-fix-more-n-key-report-descriptors-if-n-.patch
+ApplyOptionalPatch v10-0002-HID-asus-make-asus_kbd_init-generic-remove-rog_n.patch
+ApplyOptionalPatch v10-0003-HID-asus-add-ROG-Ally-N-Key-ID-and-keycodes.patch
+#ApplyOptionalPatch v10-0004-HID-asus-add-ROG-Ally-xpad-settings.patch
+ApplyOptionalPatch 0001-add-acpi_call.patch
+ApplyOptionalPatch rog-ally-bmc150.patch
+ApplyOptionalPatch uinput.patch
+ApplyOptionalPatch v2-0001-platform-x86-asus-wmi-disable-USB0-hub-on-ROG-All.patch
# hdr: https://github.com/CachyOS/kernel-patches
ApplyOptionalPatch 0001-amd-hdr.patch
-# temporary patche
-ApplyOptionalPatch 0001-Revert-PCI-Add-a-REBAR-size-quirk-for-Sapphire-RX-56.patch
+# steamdeck oled patches
+ApplyOptionalPatch steamdeck-oled-wifi.patch
+ApplyOptionalPatch steamdeck-oled-bt.patch
+ApplyOptionalPatch steamdeck-oled-audio.patch
+ApplyOptionalPatch steamdeck-oled-hw-quirks.patch
+
+# temporary patches
+ApplyOptionalPatch 0001-Remove-REBAR-size-quirk-for-Sapphire-RX-5600-XT-Puls.patch
ApplyOptionalPatch mt76:-mt7921:-Disable-powersave-features-by-default.patch
ApplyOptionalPatch 0001-acpi-proc-idle-skip-dummy-wait.patch
-# async flipping on PRIME systems can introduce problems
-ApplyOptionalPatch 0001-drm-i915-quirks-disable-async-flipping-on-specific-d.patch
-ApplyOptionalPatch 0002-drm-i915-add-kernel-parameter-to-disable-async-page-.patch
# Fixes the steam deck not coming back from hibernation
ApplyOptionalPatch 0001-Revert-nvme-pci-drop-redundant-pci_enable_pcie_error.patch
# Allows corectl to work out of the box
ApplyOptionalPatch 0001-Set-amdgpu.ppfeaturemask-0xffffffff-as-default.patch
+# steam deck: https://gitlab.com/evlaV/linux-integration/-/commit/d6935e6d62b35ec14d2187c2cfdc19934d66db6d
+ApplyOptionalPatch 0001-NOT-FOR-UPSTREAM-PM-suspend-Disable-s2idle-on-Steam-.patch
+# amdgpu bug fix: https://gitlab.freedesktop.org/drm/amd/-/issues/2733
+ApplyOptionalPatch amdgpu-bug-fix.patch
# Allow to set custom USB pollrate for specific devices like so:
# usbcore.interrupt_interval_override=045e:00db:16,1bcf:0005:1
@@ -1805,7 +1843,7 @@ ApplyOptionalPatch 0001-Set-amdgpu.ppfeaturemask-0xffffffff-as-default.patch
# https://gitlab.com/GloriousEggroll/nobara-images/-/issues/64
ApplyOptionalPatch 0001-Allow-to-set-custom-USB-pollrate-for-specific-device.patch
-# Also set the PScontroller bluetooth polling rate to 1000Hz
+# Also set the PS controller bluetooth polling rate to 1000Hz
ApplyOptionalPatch set-ps4-bt-poll-rate-1000hz.patch
%endif
@@ -3793,10 +3831,12 @@ fi\
#
#
%changelog
-* Mon Nov 27 2023 Jan Drögehoff <sentrycraft123@gmail.com> - 6.6.2-202.fsync
-- kernel-fsync v6.6.2
+* Fri Dec 01 2023 Jan Drögehoff <sentrycraft123@gmail.com> - 6.6.3-201.fsync
+- kernel-fsync v6.6.3
-* Wed Nov 22 2023 Justin M. Forbes <jforbes@fedoraproject.org> [6.6.2-201]
+* Tue Nov 28 2023 Justin M. Forbes <jforbes@fedoraproject.org> [6.6.3-0]
+- Add BugsFixed for 6.6.3 (Justin M. Forbes)
+- Update BugsFixed (Justin M. Forbes)
- Turn on USB_DWC3 for Fedora (rhbz 2250955) (Justin M. Forbes)
- Revert "netfilter: nf_tables: remove catchall element in GC sync path" (Justin M. Forbes)
- More BugsFixed (Justin M. Forbes)
@@ -3804,6 +3844,7 @@ fi\
- frop the build number back to 200 for fedora-srpm.sh (Justin M. Forbes)
- ACPI: video: Use acpi_device_fix_up_power_children() (Hans de Goede)
- ACPI: PM: Add acpi_device_fix_up_power_children() function (Hans de Goede)
+- Linux v6.6.3
* Mon Nov 20 2023 Justin M. Forbes <jforbes@fedoraproject.org> [6.6.2-0]
- Add bug for AMD ACPI alarm (Justin M. Forbes)
diff --git a/TOOLS/patch_configs.py b/TOOLS/patch_configs.py
index 01ab97d..581e925 100755
--- a/TOOLS/patch_configs.py
+++ b/TOOLS/patch_configs.py
@@ -27,12 +27,14 @@ GENERIC_PATCHES = [
["VIDEO_DW9719", None, MODULE],
["IPC_CLASSES", None, ENABLE],
["LEDS_TPS68470", None, MODULE],
- # Lenovo Legion
- ["LEGION_LAPTOP", None, MODULE],
]
ARCH_PATCHES = {
"x86_64": [
+ # Lenovo Legion
+ ["LEGION_LAPTOP", None, MODULE],
+ ["ACPI_CALL", None, MODULE],
+
# Steam Deck
["MFD_STEAMDECK", None, MODULE],
["SENSORS_STEAMDECK", None, MODULE],
@@ -81,6 +83,23 @@ ARCH_PATCHES = {
["USB_DUMMY_HCD", None, UNSET],
["USB_CONFIGFS", None, UNSET],
["PHY_SAMSUNG_USB2", None, UNSET],
+ # Deck Sound stuff?
+ ["SND_SOC_AMD_ACP_COMMON", None, MODULE],
+ ["SND_SPI", None, ENABLE],
+ ["SND_SOC_AMD_SOF_MACH", None, MODULE],
+ ["SND_SOC_AMD_MACH_COMMON", None, MODULE],
+ ["SND_SOC_SOF", None, MODULE],
+ ["SND_SOC_SOF_PROBE_WORK_QUEUE", None, ENABLE],
+ ["SND_SOC_SOF_IPC3", None, ENABLE],
+ ["SND_SOC_SOF_INTEL_IPC4", None, ENABLE],
+ ["SND_SOC_SOF_AMD_COMMON", None, MODULE],
+ ["SND_SOC_SOF_AMD_ACP63", None, MODULE],
+ ["SND_SOC_AMD_ACP_PCI", None, UNSET],
+ ["SND_AMD_ASOC_RENOIR", None, UNSET],
+ ["SND_AMD_ASOC_REMBRANDT", None, UNSET],
+ ["SND_SOC_AMD_LEGACY_MACH", None, UNSET],
+ ["SND_SOC_TOPOLOGY", None, ENABLE],
+
],
}