Overview 概述

The purpose of this guide is to show you how to disable the discrete graphics device with DSDT/SSDT edits in “switched” dual-GPU laptops (eg. Intel+Nvidia[Optimus] and Intel+Radeon).

本指南的目的是向您展示如何通过编辑 DSDT/SSDT 禁用可切换双GPU的笔记本电脑的独显(如:Intel+Nvidia[Optimus] 和 Intel+Radeon )。

Because only the Intel device can be used in these laptops, the discrete device is generally left active and using power, contributing to heat, noise, and battery usage. Although the device can usually be disabled in BIOS, it is better to disable it with a custom ACPI setup so the device can still be used when booting Windows.

由于这些笔记本电脑通常只能使用英特尔核显, 此时独显却仍然保持活动并消耗电力, 导致更大热量、噪音和电池消耗。尽管通常可以在 BIOS 中禁用该设备, 但最好通过自定义 ACPI 设置以禁用该设备, 这样在使用 Windows 时仍可使用独显。

Although it is a relatively simple patch (sometimes only a one line change), because of the fact that the patch is done to one or more SSDTs, there are many pitfalls to fall into. Also, certain machine/ACPI configurations require different approaches, leading to more complexity. The example DSDT/SSDT set that this guide uses is one of the more complex setups, so it covers most of the issues you might run into with your own.

虽然屏蔽独显是一个相对简单的补丁 (有时只有一行更改), 但由于修补涵盖一个或多个 SSDT 文件, 因此其中还是会有许多坑。此外, 某些特定机型或ACPI设置需要特定的方法, 以致情况更为复杂。本指南使用的 DSDT/SSDT 演示的是其中一种较为复杂的情况, 因此它可能涵盖了您会遇到的大多数问题。

You should follow this walkthrough with the example before patching your own.

在修补自己的DSDT前, 请通读全文并用本例进行演练。

The laptop being used for this guide/example is an “Asus UX303LN” with Intel HD4400 + Nvidia. The Clover F4 extracted tables (ACPI/origin directory) are provided in an attachment to this post.

本指南使用 Asus UX303LN 笔记本电脑作为例子,它内置了Intel HD4400 和 Nvidia 显卡。本文附件提供了通过 Clover F4 命令提取 (ACPI/origin 目录)的文件。

Note: There is also a full hotpatch guide (for this same computer) here

注: 对于该型机器,这里有一份完整的热补丁指南 (适用于同一台计算机): https://www.tonymacx86.com/threads/guide-using-clover-to-hotpatch-acpi.200137/

Patching requirements(修补要求)

The goal is quite simple. These machines provide an _OFF method, usually in an SSDT, that can be called to power down the discrete device. The easiest fix is to call _OFF from the corresponding _INI method. Note that the _OFF method may be in DSDT or may have a different name (GPOF, OPOF, _PS3, etc.)

目标很简单。这些计算机通常在 SSDT 中提供了 _OFF 方法 , 调用该方法可以关闭独显的电源。最简单的修补方法是在相应的 _INI 方法中调用 _OFF 。请注意, _OFF 方法可能位于 DSDT 中, 也可能具有不同的名称 ( GPOFOPOF_PS3等)

Certain implementations of _OFF cannot be called from _INI as they access the EC (Embedded Controller) space. For these machines, _OFF (or a portion of it) must be delayed until _REG (when Arg0==3 and Arg1==1, see ACPI spec for more information regarding _REG). In some cases, calling from _REG is too late and it is either not effective or leads to a crash. In such a case, editing _OFF to remove EC dependencies is necessary, so it can be called from _INI. The code that was removed from _OFF is then inserted into _REG so the effect is the same although the EC work is done later. This is the case with the example ACPI setup in this guide.

但某些访问了 EC (嵌入式控制器) 空间的 _OFF 实现不能从 _INI 调用。对于这些机器, _OFF (或它的一部分) 必须推迟到 _REG (当 Arg0==3 且 Arg1==1, 请参阅 ACPI 规范中有关 _REG 的细节)。有时, 在 _REG 中调用由于为时已晚, 以致修补失败或崩溃。此时, 就必须编辑 _OFF, 删除其中依赖 EC 的部分, 比如说把这部分挪到 _INI 里。即将 _OFF 中删除的代码插入到 _REG 中, 因此尽管 EC 代码执行推迟了一些, 但效果是一样的。本指南中的 ACPI 设置示例就是这种情况。

Note #1: Not all _OFF implementations have EC related code. In that case, no need to move any such code to _REG as it simply didn’t exist in the first place.

注1: 并非所有 _OFF 实现都具有与 EC 相关的代码。此时, 无需将本就不存在的此类任何代码移至 _REG

Note: #2: Not all ACPI sets have an _INI at the _OFF path. In that case, you simply add an _INI that calls off. Easiest to add it just before the _OFF method:

注2: 并非所有的 ACPI 代码在 _OFF 路径上都有 _INI。此时, 只需添加一个会调用关闭方法的 _INI。最简单的办法是在 _INI 方法最前面添加 _OFF :

Method(_INI) { _OFF() }

Basic patching 基本补丁

It is important to understand how to extract, disassemble and patch your DSDT/SSDT files, and how to install them for your bootloader, etc. This subject is covered in my guide: http://www.tonymacx86.com/yosemite-laptop-support/152573-guide-patching-laptop-dsdt-ssdts.html. You should be familiar with it before attempting this guide.

了解如何提取、反编译和修补 DSDT/SSDT 文件以及如何为引导加载程序安装这些文件及其相关知识非常重要。我的指南中涵盖了这方面内容。在尝试本指南之前, 您应该先熟悉上述链接中的相关内容。

Because patching for the basics is important, we’ll do that first here before getting started on the patching required for disabling the Nvidia card in the example.

由于基础修补很重要, 所以, 本例在演示如何通过修补禁用 Nvidia 显卡之前, 我们首先搞好基础修补。

This guide will assume that most common rename patches are done in config.plist/ACPI/DSDT/Patches. And it will assume that AutoMerge=true (relatively new Clover feature, added by RehabMan). These patches and settings are default in the guide plists: https://www.tonymacx86.com/threads/guide-booting-the-os-x-installer-on-laptops-with-clover.148093/

本指南将假定大多数常见的重命名补丁都是在 config.plist/ACPI/DSDT/Patches 中完成的。同时假设 AutoMerge=true (RehabMan 添加的, 相对较新的 Clover 功能)。这些补丁和设置在本指南的 plists 中是默认的:

In the ACPI patching guide (linked above), the kind of ACPI configuration assumed by this guide is “Partial hotpatch with patched SSDTs”, as described in “Recommended configurations”.

在 ACPI 修补指南 (上述链接) 中, 其假定的 ACPI 配置项设置为 “应用 SSDT 局部热修复补丁”, 如 “推荐配置” 中所述。

First, the files are extracted (you can simply download the example files), then disassembled with iasl:

首先, 提取文件 (您只需下载示例文件), 然后用 iasl 反汇编:

iasl -da -dl *.aml

After disassembly we can proceed to patch the various files. Because we have common renames in config.plist, and are using AutoMerge=true, we only need to apply functional patches not covered by the defaults from config.plist. And there is no need to correct errors in a file when it requires no patching. The file is, instead, simply omitted from ACPI/patched.

反编译后, 我们可以开始对各种文件进行修补。由于我们在 config. plist 中已经设置了 AutoMerge = true 和常见的重命名补丁, 所以我们只需对 config. plist 默认设置未涵盖的部分进行修补。而且, 当文件无需修补时, 就不需要更正反编译文件中的错误, 也无需放到 ACPI/patched 文件夹中。

For DSDT.dsl:

以下是适用于 DSDT.dsl 文件的常见补丁:

"Fix PARSEOP_ZERO Error (agressive)"
"Fix ADBG Error"
"Rename _DSM methods to XDSM"
"IRQ Fix"
"SMBUS Fix"
"OS Check Fix (Windows 8)"
"Add IMEI"
"RTC Fix"
"Fix _WAK Arg0 v2"
"ASUS N55SL/VivoBook"
"USB3 _PRW 0x6D (instant wake)"
"Audio Layout 12"

Since our config.plist has the common renames, none of the SSDTs need any patching, except those required to disable the discrete graphics device. At this point, we are ready to look at the SSDTs to determine the patches needed to disable the device.

由于我们的 config. plist 中已经设置了常见的重命名补丁, 因此除了禁用独显所需的补丁外, 其它 SSDT 无需作任何修补。此时, 我们已准备好查看 SSDT, 以确定禁用设备所需的修补程序。

Patching for discrete disable 屏蔽独显补丁

Remember the goal? Call _OFF from _INI

还记得目标吗?在 _INI 里调用 _OFF

Now… how to find the SSDT that has _OFF. We can use grep:

我们可以使用 grep 命令找出含有 _OFF 字符串的 SSDT 文件:

grep -l Method.*_OFF *.dsl

Which shows:

结果显示:

SSDT-7.dsl
SSDT-8.dsl

Also, it is nice to know where the _INI methods are:

同样,搜索含有 _INI 方法的文件:

grep -l Method.*_INI *.dsl

Which shows:

结果显示:

DSDT.dsl
SSDT-6.dsl
SSDT-7.dsl
SSDT-8.dsl

Note: SSDT-7 and SSDT-8 are listed again. This is likely where the _OFF and associated _INI are located…

注: SSDT-7 和 SSDT-8 出现了两次。这很可能就是 _OFF 及其关联的 _INI 所在的文件。

We could also open each file individually into MaciASL and search for _OFF and _INI, but using grep is a bit easier and faster.

我们也可以用 MaciASL 分别打开每个文件, 并搜索 _OFF_INI, 但使用 grep 更容易也更快。

When we open SSDT-7.dsl and look for “Method (_INI”, we find this:

当我们打开 SSDT-7.dsl 并查找 “Method (_INI” 时, 我们发现:

Method (_INI, 0, NotSerialized)  // _INI: Initialize
{
    Store (Zero, \_SB.PCI0.RP05.PEGP._ADR)
}

This is a typical _INI method for a discrete device, and one that we want to patch so it calls _OFF.

这是独立设备的一种典型的 _INI 方法, 我们希望对其进行修补, 使其调用 _OFF

If we click in the method body, we can see the ACPI “path” that this method lives in. MaciASL shows it on the status bar: SSDT -> \_SB.PCI0.RP05.PEGP -> _INI. Now we know that the path to _OFF should also be _SB.PCI0.RP05.PEGP._OFF.

如果我们在方法体中单击, 我们可以看到此方法所在的 ACPI “路径”。MaciASL 状态栏上显示: SSDT -> \_SB.PCI0.RP05.PEGP -> _INI。现在我们知道, 到 _OFF 的路径也应该是 \_SB.PCI0.RP05.PEGP._OFF

So, we know that _OFF is either in SSDT-7.dsl or SSDT-8.dsl. If you open SSDT-7.dsl and look for _OFF, you find it is a method inside a PowerResource macro. This is not the _OFF method we want. Open SSDT-8.dsl and look for _OFF there and you’ll find a regular _OFF method, which is what we are looking for. So now we know where _OFF is. We need to know, as it is necessary to review the code for potential EC access.

现在, 我们知道 _OFF 是在 SST-7.dsl 和 SST-8.dsl 里面。如果打开 SSDT-7.dsl 并查找 _OFF, 则会发现它是 PowerResource 宏中的一种方法。这不是我们想要的 _OFF 方法。打开 SSDT-8.dsl, 并搜索 _OFF, 你会发现一个常规的 _OFF 方法, 这就是我们要找的。所以现在我们知道 _OFF 在哪里了。我们还需检查代码, 以了解 _OFF 是否含有潜在 EC 访问。

In SSDT-8.dsl, _OFF is defined as follows:

在 SSDT-8.dsl 中, _OFF 定义如下:

Method (_OFF, 0, Serialized)  // _OFF: Power Off
{
    If (LEqual (CTXT, Zero))
    {
        \_SB.PCI0.LPCB.EC0.SPIN (0x96, Zero)
        If (LNotEqual (GPRF, One))
        {
            Store (VGAR, VGAB)
        }

        Store (One, CTXT)
    }

    SGOF ()
}

If you examine the code, you can see it accesses the EC ( \_SB.PCI0.LPCB.EC0.SPIN (0x96, Zero). This is going to be a problem and will prevent _OFF from executing fully when called from _INI. Keep in mind it is not always this obvious. The code could have called a method not directly related to the EC, which in turn calls a method in the EC (indirect access). So you may need to do some digging. This example was obvious as the method path includes EC0.

如果您检查代码, 您可以看到它访问了 EC (\_SB.PCI0.LPCB.EC0.SPIN (0x96, Zero)。这会导致以后 _INI 调用 _OFF 时无法完整执行的问题。切记, 这种情况有时并不明显。代码可以调用一个表面上与 EC 没有直接联系的方法, 但该方法内部最终会调用 EC 中的方法 (间接访问)。所以你可能需要做一些挖掘。此示例由于方法路径包括了 EC0 才很明显 。

The best way to deal with the EC access is to remove the offending code from _OFF.

处理 EC 访问的最佳方法是从 _OFF 中删除有问题的代码。

We can do it manually, or use a patch like this:

我们可以手动执行此操作, 也可以使用如下修补程序:

into method label _OFF parent_label \_SB.PCI0.RP05.PEGP code_regex .*EC.* removeall_matched;

The modified _OFF method is this:

修改后的 _OFF 方法如下:

Method (_OFF, 0, Serialized)  // _OFF: Power Off
{
    If (LEqual (CTXT, Zero))
    {
        If (LNotEqual (GPRF, One))
        {
            Store (VGAR, VGAB)
        }

        Store (One, CTXT)
    }

    SGOF ()
}

After making this change, attempt to compile SSDT-8.dsl. You will notice it has errors. Really, we should have taken care of these errors before making changes. But we can fix them now by applying the “Cleanup/Fix Errors (SSDT)” patch. That removes some of the errors, but there is still one additional error at line 620:

进行此更改后, 尝试编译 SSDT-8.dsl。你会注意到它有错误。其实, 在修改之前, 我们应该先解决掉这些错误。但我们现在可以通过使用 “Cleanup/Fix Errors (SSDT)” 补丁来修复它们。这能排除一些错误, 但在620行仍然有一个错误:

Store (TCAP, \_PR.CPU0._PTC ())

This code is invalid, and in fact will be skipped by the ACPI runtime. \_PR.CPU0._PTC is a method, and of course it is not valid to store something “into” a method. We can simply comment this code:

这行代码无效, 实际上 ACPI 运行过程中会跳过它。因为 \_PR.CPU0._PTC 是一个方法, 显然, 将某些内容 “存储到” 方法中是无效的。我们可以简单地注释掉它:

//    Store (TCAP, \_PR.CPU0._PTC ())

We should still have the line that we removed, \_SB.PCI0.LPCB.EC0.SPIN (0x96, Zero), executed in _REG, so keep that in mind.

切记, 前面我们删掉的那行代码 \_SB.PCI0.LPCB.EC0.SPIN (0x96, Zero), 需要在 _REG 中执行。

Now that we have fixed _OFF, let’s call it from _INI, defined in SSDT-7.dsl.

现在我们已经修复了 _OFF, 让我们在 SSDT-7.dsl 中定义的 _INI 里面调用它。

We can apply patch “Disable from _INI (SSDT)”. But our method’s path is slightly different from what is common, so we need to modify the patch. Also, we are calling to _OFF which is defined in SSDT-8.dsl, so we need the External declaration.

我们可以使用 "Disable from _INI (SSDT)"补丁。但我们的方法的路径与常见的方法略有不同, 所以我们需要修改补丁。此外, 我们调用的是 SSDT-8.dsl 里的 _OFF, 因此我们需要进行外部声明。

The modified patch follows:

修改后的补丁如下所示:

into method label _INI parent_label \_SB.PCI0.RP05.PEGP insert
begin
//added to turn nvidia/radeon off\n
External(\_SB.PCI0.RP05.PEGP._OFF, MethodObj)\n
_OFF()\n
end;

After applying this patch, the patched _INI now reads:

使用上述补丁后, _INI 代码如下所示:

Method (_INI, 0, NotSerialized)  // _INI: Initialize
{
    Store (Zero, \_SB.PCI0.RP05.PEGP._ADR)
    //added to turn nvidia/radeon off
    External(\_SB.PCI0.RP05.PEGP._OFF, MethodObj)
    _OFF()
}

Now we need to turn our attention to DSDT _REG. The _REG method needs to do the EC work that _OFF used to do before we patched it.

现在, 我们把注意力转向 DSDT _REG_REG 方法需要执行 _OFF 在修补之前所做的 EC 操作。

Here is the original _REG (for EC0) in DSDT:

下面是 DSDT 中的原始 _REG (对于 EC0):

Method (_REG, 2, NotSerialized)  // _REG: Region Availability
{
    If (LEqual (Arg0, 0x03))
    {
        Store (Arg1, ECFL)
    }
}

There is a patch in my repo to call _OFF from _REG. We can use it as a base for what we need:

我的存储库有一个用于 _REG 调用 _OFF 的补丁, 可以把它作为我们要的补丁的基础:

into method label _REG parent_hid PNP0C09 insert
begin
//added to turn nvidia/radeon off\n
If (LAnd(LEqual(Arg0,3),LEqual(Arg1,1)))\n
{\n
    External(\_SB.PCI0.PEG0.PEGP._OFF, MethodObj)\n
    \_SB.PCI0.PEG0.PEGP._OFF()\n
}\n
end;

Instead of calling _OFF, we want it to call SPIN as the original _OFF did.

我们不希望它调用 _OFF, 而是像原来的 _OFF 一样调用 SPIN。

The modified patch is:

修改后的补丁代码如下:

into method label _REG parent_hid PNP0C09 insert
begin
//added to turn nvidia/radeon off\n
If (LAnd(LEqual(Arg0,3),LEqual(Arg1,1)))\n
{\n
    \_SB.PCI0.LPCB.EC0.SPIN (0x96, Zero)\n
}\n
end;

The modified _REG after patching:

修改后的 _REG 代码如下:

Method (_REG, 2, NotSerialized)  // _REG: Region Availability
{
    If (LEqual (Arg0, 0x03))
    {
        Store (Arg1, ECFL)
    }
    //added to turn nvidia/radeon off
    If (LAnd(LEqual(Arg0,3),LEqual(Arg1,1)))
    {
        \_SB.PCI0.LPCB.EC0.SPIN (0x96, Zero)
    }
}

And that concludes patching. At this point all the patched files can be compiled and placed in ACPI/patched.

这就完成了本篇所要实现的屏蔽独显补丁。此时, 可以编译所有修补过的文件, 并将其放到 ACPI/patched 文件夹。

Only the patched files are placed in ACPI/patched:

ACPI/patched 里面只需放置以下三个修补过的文件:

DSDT.aml (has core patches, _REG patch)(有核心补丁, _REG 补丁)
SSDT-7.aml (has _INI patch)(有 _INI补丁)
SSDT-8.aml (has _OFF patch)(有_OFF 补丁)

Note that this example is complex and not all laptops will have the same configuration. For most, the _INI and _OFF associated with the discrete device are in the same SSDT. In that case it is not necessary to use the External declaration. You can use the “Call _OFF from _INI (SSDT)” patch directly. Some even have it in DSDT (again you can use the patch directly). And not all _OFF methods access the EC, so the movement of the EC related code to _REG is not needed.

请注意, 此示例很复杂, 并非所有笔记本电脑都具有相同的配置。大多数情况下, 与独立设备关联的 _INI_OFF 位于同一个 SSDT 中。此时, 不需要使用外部声明。您可以直接使用 “Call _OFF from _INI (SSDT)” 补丁。有时, 甚至 DSDT 中也有它 (同样, 您可以直接使用补丁)。而且并非所有 _OFF 方法都访问 EC, 此时不需要将 EC 相关代码移到 _REG

It is also possible that the EC related code is more than one line. All such code should be moved to _REG.

也有可能 EC 相关代码是多行的。所有这些代码都应移动到 _REG

Sleep/Wake Problems 睡眠 / 唤醒问题

Some laptops have issues with sleep/wake or even shutdown/restart with this patch in place. This was the case with the HP ProBook (with Radeon). The fix is to re-enable the card before sleeping and disable it on wake.

在应用上述补丁后, 一些笔记本电脑睡眠 / 唤醒出现问题, 甚至关机 / 重启也出现了问题。HP ProBook (内置 Radeon显卡) 的情况就是如此。解决方法是在睡眠前重新启用该卡, 并在唤醒时再将其禁用。

There is a patch in the repository to accomplish this. It is called “Disable/Enable on _WAK/_PTS (DSDT)”.

存储库中有一个称为 "Disable/Enable on _WAK/_PTS (DSDT)"补丁用于解决上述问题。

But since this DSDT has _OFF/_ON at a different path than the patch expects, we need to modify it.

但是, 由于此 DSDT 里面的 _OFF/_ON 与实际补丁所预期的路径不同, 我们需要对其进行修改。

Original patch:

原始补丁:

into method label _PTS code_regex ([\s\S]*) replace_matched
begin
External(\\_SB.PCI0.PEG0.PEGP._ON, MethodObj)\n
If (CondRefOf(\\_SB.PCI0.PEG0.PEGP._ON)) { \\_SB.PCI0.PEG0.PEGP._ON() }\n
%1
end;

into method label _WAK code_regex (Return\s+\(.*) replace_matched
begin
External(\\_SB.PCI0.PEG0.PEGP._OFF, MethodObj)\n
If (CondRefOf(\\_SB.PCI0.PEG0.PEGP._OFF)) { \\_SB.PCI0.PEG0.PEGP._OFF() }\n
%1
end;

Modified patch (PEG0 changed to RP05):

将补丁的 PEG0 修改为 RP05 :

into method label _PTS code_regex ([\s\S]*) replace_matched
begin
External(\\_SB.PCI0.RP05.PEGP._ON, MethodObj)\n
If (CondRefOf(\\_SB.PCI0.RP05.PEGP._ON)) { \\_SB.PCI0.RP05.PEGP._ON() }\n
%1
end;

into method label _WAK code_regex (Return\s+\(.*) replace_matched
begin
External(\\_SB.PCI0.RP05.PEGP._OFF, MethodObj)\n
If (CondRefOf(\\_SB.PCI0.RP05.PEGP._OFF)) { \\_SB.PCI0.RP05.PEGP._OFF() }\n
%1
end;

If your laptop doesn’t have problems with sleep/wake/restart/shutdown with the discrete device disabled, then you do not need this patch. You need to test your laptop first to determine if it is needed.

如果您的笔记本电脑在禁用独显时没有上述睡眠 / 唤醒 / 关机 / 重启问题, 则不需要此修补程序。您需要先测试您的笔记本电脑, 以确定是否需要它。

Note: If you patched _OFF to remove EC related code, and you’re calling _OFF from _WAK, you may need to also execute that removed code in _WAK. One way to do that is to have an XOFF that is the original/un-patched _OFF that you can call from _WAK instead of calling the (patched) _OFF.

注: 如果您修补 _OFF 以删除与 EC 相关的代码, 并且正在从 _WAK 调用 _OFF, 则可能还需要在 _WAK 中执行删除的代码。有一个 XOFF 方法, 它实际上是原始/未修补的 _OFF, 您可以从 _WAK 调用它, 而不是调用 (修补过的) _OFF

Older laptops 旧型号笔记本电脑

You will notice that older laptops may not have _OFF, or may have an empty _OFF. In such cases, you may notice that _PS3 is required. In some cases, _PS3 will not work unless the invoke the associated _DSM with appropriate params. Details are in post #96 of this thread: http://www.tonymacx86.com/yosemite-laptop-support/163772-guide-disabling-discrete-graphics-dual-gpu-laptops-10.html#post1056748

您会注意到, 较旧的笔记本电脑可能没有 _OFF, 或 _OFF 可能是空的。此时, 您可能需要关注 _PS3。有时, 调用_PS3 也不起作用, 除非用特定的参数调用关联的 _DSM 。详情参见帖子的 #96 楼:

Problem Reporting 报告问题

If you have problems with your DSDT/SSDTs in patching or fixing errors, please provide all the native files that you’re working with.

如果您在修补或修复 DSDT/SSDT 错误方面遇到问题, 请提供您正在处理的所有本机文件

If you attempt to disable your discrete card according to this guide and it is not working, please provide “Problem Reporting” data as per FAQ.

如果您按照本指南尝试禁用独显, 但无效, 请根据下面链接给出的 FAQ 指引, 提供数据 。

Read FAQ, “Problem Reporting”. Carefully. Attach all requested files/output.

在提问前, 请务必仔细阅读 FAQ 里面的常见问题解答, 并在"报告问题"帖子中添加所有提问所需文件或生成文件。

Sample Attachment (Asus ux303ln 原始DSDT/SSDT)

The following files are used as the example ACPI set in this guide: