mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
13 KiB
513 lines
13 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* osi.c - _OSI implementation |
|
* |
|
* Copyright (C) 2016 Intel Corporation |
|
* Author: Lv Zheng <[email protected]> |
|
*/ |
|
|
|
/* Uncomment next line to get verbose printout */ |
|
/* #define DEBUG */ |
|
#define pr_fmt(fmt) "ACPI: " fmt |
|
|
|
#include <linux/module.h> |
|
#include <linux/kernel.h> |
|
#include <linux/acpi.h> |
|
#include <linux/dmi.h> |
|
#include <linux/platform_data/x86/apple.h> |
|
|
|
#include "internal.h" |
|
|
|
|
|
#define OSI_STRING_LENGTH_MAX 64 |
|
#define OSI_STRING_ENTRIES_MAX 16 |
|
|
|
struct acpi_osi_entry { |
|
char string[OSI_STRING_LENGTH_MAX]; |
|
bool enable; |
|
}; |
|
|
|
static struct acpi_osi_config { |
|
u8 default_disabling; |
|
unsigned int linux_enable:1; |
|
unsigned int linux_dmi:1; |
|
unsigned int linux_cmdline:1; |
|
unsigned int darwin_enable:1; |
|
unsigned int darwin_dmi:1; |
|
unsigned int darwin_cmdline:1; |
|
} osi_config; |
|
|
|
static struct acpi_osi_config osi_config; |
|
static struct acpi_osi_entry |
|
osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = { |
|
{"Module Device", true}, |
|
{"Processor Device", true}, |
|
{"3.0 _SCP Extensions", true}, |
|
{"Processor Aggregator Device", true}, |
|
/* |
|
* Linux-Dell-Video is used by BIOS to disable RTD3 for NVidia graphics |
|
* cards as RTD3 is not supported by drivers now. Systems with NVidia |
|
* cards will hang without RTD3 disabled. |
|
* |
|
* Once NVidia drivers officially support RTD3, this _OSI strings can |
|
* be removed if both new and old graphics cards are supported. |
|
*/ |
|
{"Linux-Dell-Video", true}, |
|
/* |
|
* Linux-Lenovo-NV-HDMI-Audio is used by BIOS to power on NVidia's HDMI |
|
* audio device which is turned off for power-saving in Windows OS. |
|
* This power management feature observed on some Lenovo Thinkpad |
|
* systems which will not be able to output audio via HDMI without |
|
* a BIOS workaround. |
|
*/ |
|
{"Linux-Lenovo-NV-HDMI-Audio", true}, |
|
/* |
|
* Linux-HPI-Hybrid-Graphics is used by BIOS to enable dGPU to |
|
* output video directly to external monitors on HP Inc. mobile |
|
* workstations as Nvidia and AMD VGA drivers provide limited |
|
* hybrid graphics supports. |
|
*/ |
|
{"Linux-HPI-Hybrid-Graphics", true}, |
|
}; |
|
|
|
static u32 acpi_osi_handler(acpi_string interface, u32 supported) |
|
{ |
|
if (!strcmp("Linux", interface)) { |
|
pr_notice_once(FW_BUG |
|
"BIOS _OSI(Linux) query %s%s\n", |
|
osi_config.linux_enable ? "honored" : "ignored", |
|
osi_config.linux_cmdline ? " via cmdline" : |
|
osi_config.linux_dmi ? " via DMI" : ""); |
|
} |
|
if (!strcmp("Darwin", interface)) { |
|
pr_notice_once( |
|
"BIOS _OSI(Darwin) query %s%s\n", |
|
osi_config.darwin_enable ? "honored" : "ignored", |
|
osi_config.darwin_cmdline ? " via cmdline" : |
|
osi_config.darwin_dmi ? " via DMI" : ""); |
|
} |
|
|
|
return supported; |
|
} |
|
|
|
void __init acpi_osi_setup(char *str) |
|
{ |
|
struct acpi_osi_entry *osi; |
|
bool enable = true; |
|
int i; |
|
|
|
if (!acpi_gbl_create_osi_method) |
|
return; |
|
|
|
if (str == NULL || *str == '\0') { |
|
pr_info("_OSI method disabled\n"); |
|
acpi_gbl_create_osi_method = FALSE; |
|
return; |
|
} |
|
|
|
if (*str == '!') { |
|
str++; |
|
if (*str == '\0') { |
|
/* Do not override acpi_osi=!* */ |
|
if (!osi_config.default_disabling) |
|
osi_config.default_disabling = |
|
ACPI_DISABLE_ALL_VENDOR_STRINGS; |
|
return; |
|
} else if (*str == '*') { |
|
osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS; |
|
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { |
|
osi = &osi_setup_entries[i]; |
|
osi->enable = false; |
|
} |
|
return; |
|
} else if (*str == '!') { |
|
osi_config.default_disabling = 0; |
|
return; |
|
} |
|
enable = false; |
|
} |
|
|
|
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { |
|
osi = &osi_setup_entries[i]; |
|
if (!strcmp(osi->string, str)) { |
|
osi->enable = enable; |
|
break; |
|
} else if (osi->string[0] == '\0') { |
|
osi->enable = enable; |
|
strncpy(osi->string, str, OSI_STRING_LENGTH_MAX); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
static void __init __acpi_osi_setup_darwin(bool enable) |
|
{ |
|
osi_config.darwin_enable = !!enable; |
|
if (enable) { |
|
acpi_osi_setup("!"); |
|
acpi_osi_setup("Darwin"); |
|
} else { |
|
acpi_osi_setup("!!"); |
|
acpi_osi_setup("!Darwin"); |
|
} |
|
} |
|
|
|
static void __init acpi_osi_setup_darwin(bool enable) |
|
{ |
|
/* Override acpi_osi_dmi_blacklisted() */ |
|
osi_config.darwin_dmi = 0; |
|
osi_config.darwin_cmdline = 1; |
|
__acpi_osi_setup_darwin(enable); |
|
} |
|
|
|
/* |
|
* The story of _OSI(Linux) |
|
* |
|
* From pre-history through Linux-2.6.22, Linux responded TRUE upon a BIOS |
|
* OSI(Linux) query. |
|
* |
|
* Unfortunately, reference BIOS writers got wind of this and put |
|
* OSI(Linux) in their example code, quickly exposing this string as |
|
* ill-conceived and opening the door to an un-bounded number of BIOS |
|
* incompatibilities. |
|
* |
|
* For example, OSI(Linux) was used on resume to re-POST a video card on |
|
* one system, because Linux at that time could not do a speedy restore in |
|
* its native driver. But then upon gaining quick native restore |
|
* capability, Linux has no way to tell the BIOS to skip the time-consuming |
|
* POST -- putting Linux at a permanent performance disadvantage. On |
|
* another system, the BIOS writer used OSI(Linux) to infer native OS |
|
* support for IPMI! On other systems, OSI(Linux) simply got in the way of |
|
* Linux claiming to be compatible with other operating systems, exposing |
|
* BIOS issues such as skipped device initialization. |
|
* |
|
* So "Linux" turned out to be a really poor chose of OSI string, and from |
|
* Linux-2.6.23 onward we respond FALSE. |
|
* |
|
* BIOS writers should NOT query _OSI(Linux) on future systems. Linux will |
|
* complain on the console when it sees it, and return FALSE. To get Linux |
|
* to return TRUE for your system will require a kernel source update to |
|
* add a DMI entry, or boot with "acpi_osi=Linux" |
|
*/ |
|
static void __init __acpi_osi_setup_linux(bool enable) |
|
{ |
|
osi_config.linux_enable = !!enable; |
|
if (enable) |
|
acpi_osi_setup("Linux"); |
|
else |
|
acpi_osi_setup("!Linux"); |
|
} |
|
|
|
static void __init acpi_osi_setup_linux(bool enable) |
|
{ |
|
/* Override acpi_osi_dmi_blacklisted() */ |
|
osi_config.linux_dmi = 0; |
|
osi_config.linux_cmdline = 1; |
|
__acpi_osi_setup_linux(enable); |
|
} |
|
|
|
/* |
|
* Modify the list of "OS Interfaces" reported to BIOS via _OSI |
|
* |
|
* empty string disables _OSI |
|
* string starting with '!' disables that string |
|
* otherwise string is added to list, augmenting built-in strings |
|
*/ |
|
static void __init acpi_osi_setup_late(void) |
|
{ |
|
struct acpi_osi_entry *osi; |
|
char *str; |
|
int i; |
|
acpi_status status; |
|
|
|
if (osi_config.default_disabling) { |
|
status = acpi_update_interfaces(osi_config.default_disabling); |
|
if (ACPI_SUCCESS(status)) |
|
pr_info("Disabled all _OSI OS vendors%s\n", |
|
osi_config.default_disabling == |
|
ACPI_DISABLE_ALL_STRINGS ? |
|
" and feature groups" : ""); |
|
} |
|
|
|
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { |
|
osi = &osi_setup_entries[i]; |
|
str = osi->string; |
|
if (*str == '\0') |
|
break; |
|
if (osi->enable) { |
|
status = acpi_install_interface(str); |
|
if (ACPI_SUCCESS(status)) |
|
pr_info("Added _OSI(%s)\n", str); |
|
} else { |
|
status = acpi_remove_interface(str); |
|
if (ACPI_SUCCESS(status)) |
|
pr_info("Deleted _OSI(%s)\n", str); |
|
} |
|
} |
|
} |
|
|
|
static int __init osi_setup(char *str) |
|
{ |
|
if (str && !strcmp("Linux", str)) |
|
acpi_osi_setup_linux(true); |
|
else if (str && !strcmp("!Linux", str)) |
|
acpi_osi_setup_linux(false); |
|
else if (str && !strcmp("Darwin", str)) |
|
acpi_osi_setup_darwin(true); |
|
else if (str && !strcmp("!Darwin", str)) |
|
acpi_osi_setup_darwin(false); |
|
else |
|
acpi_osi_setup(str); |
|
|
|
return 1; |
|
} |
|
__setup("acpi_osi=", osi_setup); |
|
|
|
bool acpi_osi_is_win8(void) |
|
{ |
|
return acpi_gbl_osi_data >= ACPI_OSI_WIN_8; |
|
} |
|
EXPORT_SYMBOL(acpi_osi_is_win8); |
|
|
|
static void __init acpi_osi_dmi_darwin(void) |
|
{ |
|
pr_notice("DMI detected to setup _OSI(\"Darwin\"): Apple hardware\n"); |
|
osi_config.darwin_dmi = 1; |
|
__acpi_osi_setup_darwin(true); |
|
} |
|
|
|
static void __init acpi_osi_dmi_linux(bool enable, |
|
const struct dmi_system_id *d) |
|
{ |
|
pr_notice("DMI detected to setup _OSI(\"Linux\"): %s\n", d->ident); |
|
osi_config.linux_dmi = 1; |
|
__acpi_osi_setup_linux(enable); |
|
} |
|
|
|
static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) |
|
{ |
|
acpi_osi_dmi_linux(true, d); |
|
|
|
return 0; |
|
} |
|
|
|
static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) |
|
{ |
|
pr_notice("DMI detected: %s\n", d->ident); |
|
acpi_osi_setup("!Windows 2006"); |
|
acpi_osi_setup("!Windows 2006 SP1"); |
|
acpi_osi_setup("!Windows 2006 SP2"); |
|
|
|
return 0; |
|
} |
|
|
|
static int __init dmi_disable_osi_win7(const struct dmi_system_id *d) |
|
{ |
|
pr_notice("DMI detected: %s\n", d->ident); |
|
acpi_osi_setup("!Windows 2009"); |
|
|
|
return 0; |
|
} |
|
|
|
static int __init dmi_disable_osi_win8(const struct dmi_system_id *d) |
|
{ |
|
pr_notice("DMI detected: %s\n", d->ident); |
|
acpi_osi_setup("!Windows 2012"); |
|
|
|
return 0; |
|
} |
|
|
|
/* |
|
* Linux default _OSI response behavior is determined by this DMI table. |
|
* |
|
* Note that _OSI("Linux")/_OSI("Darwin") determined here can be overridden |
|
* by acpi_osi=!Linux/acpi_osi=!Darwin command line options. |
|
*/ |
|
static const struct dmi_system_id acpi_osi_dmi_table[] __initconst = { |
|
{ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "Fujitsu Siemens", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), |
|
}, |
|
}, |
|
{ |
|
/* |
|
* There have a NVIF method in MSI GX723 DSDT need call by Nvidia |
|
* driver (e.g. nouveau) when user press brightness hotkey. |
|
* Currently, nouveau driver didn't do the job and it causes there |
|
* have a infinite while loop in DSDT when user press hotkey. |
|
* We add MSI GX723's dmi information to this table for workaround |
|
* this issue. |
|
* Will remove MSI GX723 from the table after nouveau grows support. |
|
*/ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "MSI GX723", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "GX723"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "Sony VGN-NS10J_S", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "Sony VGN-SR290J", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "VGN-NS50B_L", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "VGN-SR19XN", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "Toshiba Satellite L355", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), |
|
DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_win7, |
|
.ident = "ASUS K50IJ", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "Toshiba P305D", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_vista, |
|
.ident = "Toshiba NB100", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "NB100"), |
|
}, |
|
}, |
|
|
|
/* |
|
* The wireless hotkey does not work on those machines when |
|
* returning true for _OSI("Windows 2012") |
|
*/ |
|
{ |
|
.callback = dmi_disable_osi_win8, |
|
.ident = "Dell Inspiron 7737", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_win8, |
|
.ident = "Dell Inspiron 7537", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_win8, |
|
.ident = "Dell Inspiron 5437", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_win8, |
|
.ident = "Dell Inspiron 3437", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_win8, |
|
.ident = "Dell Vostro 3446", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"), |
|
}, |
|
}, |
|
{ |
|
.callback = dmi_disable_osi_win8, |
|
.ident = "Dell Vostro 3546", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"), |
|
}, |
|
}, |
|
|
|
/* |
|
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug. |
|
* Linux ignores it, except for the machines enumerated below. |
|
*/ |
|
|
|
/* |
|
* Without this EEEpc exports a non working WMI interface, with |
|
* this it exports a working "good old" eeepc_laptop interface, |
|
* fixing both brightness control, and rfkill not working. |
|
*/ |
|
{ |
|
.callback = dmi_enable_osi_linux, |
|
.ident = "Asus EEE PC 1015PX", |
|
.matches = { |
|
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), |
|
DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"), |
|
}, |
|
}, |
|
{} |
|
}; |
|
|
|
static __init void acpi_osi_dmi_blacklisted(void) |
|
{ |
|
dmi_check_system(acpi_osi_dmi_table); |
|
|
|
/* Enable _OSI("Darwin") for Apple platforms. */ |
|
if (x86_apple_machine) |
|
acpi_osi_dmi_darwin(); |
|
} |
|
|
|
int __init early_acpi_osi_init(void) |
|
{ |
|
acpi_osi_dmi_blacklisted(); |
|
|
|
return 0; |
|
} |
|
|
|
int __init acpi_osi_init(void) |
|
{ |
|
acpi_install_interface_handler(acpi_osi_handler); |
|
acpi_osi_setup_late(); |
|
|
|
return 0; |
|
}
|
|
|