diff options
author | Jan200101 <sentrycraft123@gmail.com> | 2023-12-06 19:18:31 +0100 |
---|---|---|
committer | Jan200101 <sentrycraft123@gmail.com> | 2023-12-06 19:18:31 +0100 |
commit | 35570801b7384f86c61fccf98cdf080d82ffd182 (patch) | |
tree | da74997436cce94ba6a3530269fbeb6ef9d8cd98 /SOURCES | |
parent | 6229f58d19ef9e8c060cc9d9974ef6fcf1bcb528 (diff) | |
download | kernel-fsync-35570801b7384f86c61fccf98cdf080d82ffd182.tar.gz kernel-fsync-35570801b7384f86c61fccf98cdf080d82ffd182.zip |
kernel 6.6.3
Diffstat (limited to 'SOURCES')
50 files changed, 11973 insertions, 828 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, ®id); - 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, ®_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, ®id); - 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, ®_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 *)×tamp, ++ (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, ®); ++ + 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(¶m, 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, ¶m); ++} ++ ++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(¶m, 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, ¶m); ++} ++ + 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(¶m, 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, ¶m); ++ 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(¶ms->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, ++ ¢er_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, ++ ¢er_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, ++ ¢er_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, ++ ¢er_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, ++ ¢er_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, ++ ¢er_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(¶ms->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(¶ms->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 + |