diff --git a/src/drivers/gfx/nvidia/Kconfig b/src/drivers/gfx/nvidia/Kconfig new file mode 100644 index 0000000000..4c9c7ff08c --- /dev/null +++ b/src/drivers/gfx/nvidia/Kconfig @@ -0,0 +1,38 @@ +config DRIVERS_GFX_NVIDIA + bool + default n + help + Support for NVIDIA Optimus graphics + +config DRIVERS_GFX_NVIDIA_BRIDGE + hex "PCI bridge for the GPU device" + default 0x01 + depends on DRIVERS_GFX_NVIDIA + +config DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST + depends on DRIVERS_GFX_NVIDIA + bool + default n + help + Support for NVIDIA Dynamic Boost + +config DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST_TPP + int "Total processor power offset from default TGP in watts" + default 45 + depends on DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST + help + This identifies the available power for the CPU or GPU boost + +config DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST_MIN + int "Minimum TGP offset from default TGP in watts" + default 0 + depends on DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST + help + This is used to transfer power from the GPU to the CPU + +config DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST_MAX + int "Maximum TGP offset from default TGP in watts" + default 0 + depends on DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST + help + This is used to transfer power from the CPU to the GPU diff --git a/src/drivers/gfx/nvidia/Makefile.mk b/src/drivers/gfx/nvidia/Makefile.mk new file mode 100644 index 0000000000..32ddf7e64e --- /dev/null +++ b/src/drivers/gfx/nvidia/Makefile.mk @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +romstage-$(CONFIG_DRIVERS_GFX_NVIDIA) += romstage.c + +ramstage-$(CONFIG_DRIVERS_GFX_NVIDIA) += nvidia.c diff --git a/src/drivers/gfx/nvidia/acpi/coffeelake.asl b/src/drivers/gfx/nvidia/acpi/coffeelake.asl new file mode 100644 index 0000000000..c515ec1cb2 --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/coffeelake.asl @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* NVIDIA GC6 on CFL and CML CPU PCIe ports */ + +// Memory mapped PCI express config space +OperationRegion (PCIC, SystemMemory, CONFIG_ECAM_MMCONF_BASE_ADDRESS + (CONFIG_DRIVERS_GFX_NVIDIA_BRIDGE << 15), 0x1000) + +Field (PCIC, ByteAcc, NoLock, Preserve) { + PVID, 16, + PDID, 16, + + Offset (0x248), + , 7, + L23E, 1, /* L23_Rdy Entry Request */ + L23R, 1, /* L23_Rdy to Detect Transition */ + + Offset (0xC20), + , 4, + P0AP, 2, /* Additional power savings */ + + Offset (0xC38), + , 3, + P0RM, 1, /* Robust squelch mechanism */ +} + +// Enter L23 +Method (DL23, 0, Serialized) { + Printf(" GPU PORT DL23 START") + + L23E = 1 + Sleep (16) + Local0 = 0 + While (L23E) { + If ((Local0 > 4)) { + Break + } + + Sleep (16) + Local0++ + } + + P0RM = 1 + P0AP = 3 + + Printf(" GPU PORT DL23 FINISH") +} + +// Exit L23 +Method (L23D, 0, Serialized) { + Printf(" GPU PORT L23D START") + + L23R = 1 + Sleep (16) + Local0 = 0 + While (L23R) { + If ((Local0 > 4)) { + Break + } + + Sleep (16) + Local0++ + } + + P0RM = 0 + P0AP = 0 + + Printf(" GPU PORT L23D FINISH") +} + +// Main power resource +PowerResource (PWRR, 0, 0) { + Name (_STA, 1) + + Method (_ON, 0, Serialized) { + Printf("GPU PORT PWRR._ON") + + ^^DEV0._ON() + + _STA = 1 + } + + Method (_OFF, 0, Serialized) { + Printf("GPU PORT PWRR._OFF") + + ^^DEV0._OFF() + + _STA = 0 + } +} + +// Power resources for entering D0 +Name (_PR0, Package () { PWRR }) + +// Power resources for entering D3 +Name (_PR3, Package () { PWRR }) + +#include "common/gpu.asl" diff --git a/src/drivers/gfx/nvidia/acpi/common/dsm.asl b/src/drivers/gfx/nvidia/acpi/common/dsm.asl new file mode 100644 index 0000000000..ad440b58b7 --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/common/dsm.asl @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define NV_ERROR_SUCCESS 0x0 +#define NV_ERROR_UNSPECIFIED 0x80000001 +#define NV_ERROR_UNSUPPORTED 0x80000002 + +#include "gps.asl" +#include "nvjt.asl" + +Method (_DSM, 4, Serialized) { + Printf("GPU _DSM") + If (Arg0 == ToUUID (JT_DSM_GUID)) { + If (ToInteger(Arg1) >= JT_REVISION_ID_MIN) { + Return (NVJT(Arg2, Arg3)) + } Else { + Printf(" Unsupported JT revision: %o", SFST(Arg1)) + Return (NV_ERROR_UNSUPPORTED) + } + } ElseIf (Arg0 == ToUUID (GPS_DSM_GUID)) { + If (ToInteger(Arg1) == GPS_REVISION_ID) { + Return (GPS(Arg2, Arg3)) + } Else { + Printf(" Unsupported GPS revision: %o", SFST(Arg1)) + Return (NV_ERROR_UNSUPPORTED) + } + } Else { + Printf(" Unsupported GUID: %o", IDST(Arg0)) + Return (NV_ERROR_UNSPECIFIED) + } +} diff --git a/src/drivers/gfx/nvidia/acpi/common/gps.asl b/src/drivers/gfx/nvidia/acpi/common/gps.asl new file mode 100644 index 0000000000..7d69b19406 --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/common/gps.asl @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define GPS_DSM_GUID "A3132D01-8CDA-49BA-A52E-BC9D46DF6B81" +#define GPS_REVISION_ID 0x00000200 +#define GPS_FUNC_SUPPORT 0x00000000 +#define GPS_FUNC_PSHARESTATUS 0x00000020 +#define GPS_FUNC_PSHAREPARAMS 0x0000002A + +Method(GPS, 2, Serialized) { + Printf(" GPU GPS") + Switch(ToInteger(Arg0)) { + Case(GPS_FUNC_SUPPORT) { + Printf(" Supported Functions") + Return(ITOB( + (1 << GPS_FUNC_SUPPORT) | + (1 << GPS_FUNC_PSHARESTATUS) | + (1 << GPS_FUNC_PSHAREPARAMS) + )) + } + Case(GPS_FUNC_PSHARESTATUS) { + Printf(" Power Share Status") + Return(ITOB(0)) + } + Case(GPS_FUNC_PSHAREPARAMS) { + Printf(" Power Share Parameters") + + CreateField(Arg1, 0, 4, QTYP) // Query type + + Name(GPSP, Buffer(36) { 0x00 }) + CreateDWordField(GPSP, 0, RSTS) // Response status + CreateDWordField(GPSP, 4, VERS) // Version + + // Set query type of response + RSTS = QTYP + // Set version of response + VERS = 0x00010000 + + Switch(ToInteger(QTYP)) { + Case(0) { + Printf(" Request Current Information") + // No required information + Return(GPSP) + } + Case(1) { + Printf(" Request Supported Fields") + // Support GPU temperature field + RSTS |= (1 << 8) + Return(GPSP) + } + Case(2) { + Printf(" Request Current Limits") + // No required limits + Return(GPSP) + } + Default { + Printf(" Unknown Query: %o", SFST(QTYP)) + Return(NV_ERROR_UNSUPPORTED) + } + } + } + Default { + Printf(" Unsupported function: %o", SFST(Arg0)) + Return(NV_ERROR_UNSUPPORTED) + } + } +} diff --git a/src/drivers/gfx/nvidia/acpi/common/gpu.asl b/src/drivers/gfx/nvidia/acpi/common/gpu.asl new file mode 100644 index 0000000000..49e5c47285 --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/common/gpu.asl @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (DEV0) { + Name(_ADR, 0x00000000) + + #include "utility.asl" + #include "dsm.asl" + #include "power.asl" +} + +#if CONFIG(DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST) +Scope (\_SB) { + Device(NPCF) { + #include "utility.asl" + #include "nvpcf.asl" + } +} +#endif diff --git a/src/drivers/gfx/nvidia/acpi/common/nvjt.asl b/src/drivers/gfx/nvidia/acpi/common/nvjt.asl new file mode 100644 index 0000000000..31eaed275e --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/common/nvjt.asl @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define JT_DSM_GUID "CBECA351-067B-4924-9CBD-B46B00B86F34" +#define JT_REVISION_ID_MIN 0x00000100 +#define JT_REVISION_ID_MAX 0x00000200 +#define JT_FUNC_SUPPORT 0x00000000 +#define JT_FUNC_CAPS 0x00000001 +#define JT_FUNC_POWERCONTROL 0x00000003 + +//TODO: SMI traps and EGIN/XCLM +#define JT_GPC_GSS 0 // Get current GPU GCx sleep status +#define JT_GPC_EGNS 1 // Enter GC6 without self-refresh +#define JT_GPC_EGIS 2 // Enter GC6 with self-refresh +#define JT_GPC_XGXS 3 // Exit GC6 and stop self-refresh +#define JT_GPC_XGIS 4 // Exit GC6 for self-refresh update + +#define JT_DFGC_NONE 0 // Handle request immediately +#define JT_DFGC_DEFER 1 // Defer GPC and GPCX +//TODO #define JT_DFGC_CLEAR 2 // Clear pending requests + +// Deferred GC6 enter/exit until D3-cold (saved DFGC) +Name(DFEN, 0) + +// Deferred GC6 enter control (saved GPC) +Name(DFCI, 0) + +// Deferred GC6 exit control (saved GPCX) +Name(DFCO, 0) + +Method (NVJT, 2, Serialized) { + Printf(" GPU NVJT") + Switch (ToInteger(Arg0)) { + Case (JT_FUNC_SUPPORT) { + Printf(" Supported Functions") + Return(ITOB( + (1 << JT_FUNC_SUPPORT) | + (1 << JT_FUNC_CAPS) | + (1 << JT_FUNC_POWERCONTROL) + )) + } + Case (JT_FUNC_CAPS) { + Printf(" Capabilities") + Return(ITOB( + (1 << 0) | // G-SYNC NSVR power-saving features are enabled + (1 << 1) | // NVSR disabled + (2 << 3) | // Panel power and backlight are on the suspend rail + (0 << 5) | // self-refresh controller remains powered while panel is powered + (0 << 6) | // FB is not on the suspend rail but is powered on in GC6 + (0 << 8) | // Combined power rail for all GPUs + (0 << 10) | // External SPI ROM + (1 << 11) | // No SMI handler for kernel panic exit while in GC6 + (0 << 12) | // Supports notify on GC6 state done + (1 << 13) | // Support deferred GC6 + (1 << 14) | // Support fine-grained root port control + (2 << 15) | // GC6 version is GC6-R + (0 << 17) | // GC6 exit ISR is not supported + (0 << 18) | // GC6 self wakeup not supported + (JT_REVISION_ID_MAX << 20) // Highest revision supported + )) + } + Case (JT_FUNC_POWERCONTROL) { + Printf(" Power Control: %o", SFST(Arg1)) + + CreateField (Arg1, 0, 3, GPC) // GPU power control + CreateField (Arg1, 4, 1, PPC) // Panel power control + CreateField (Arg1, 14, 2, DFGC) // Defer GC6 enter/exit until D3 cold + CreateField (Arg1, 16, 3, GPCX) // Deferred GC6 exit control + + // Save deferred GC6 request + If ((ToInteger(GPC) != 0) || (ToInteger(DFGC) != 0)) { + DFEN = DFGC + DFCI = GPC + DFCO = GPCX + } + + // Buffer to cache current state + Name (JTBF, Buffer (4) { 0, 0, 0, 0 }) + CreateField (JTBF, 0, 3, CGCS) // Current GC state + CreateField (JTBF, 3, 1, CGPS) // Current GPU power status + CreateField (JTBF, 7, 1, CPSS) // Current panel and SRC state (0 when on) + + // If doing deferred GC6 request, return now + If (ToInteger(DFGC) != 0) { + CGCS = 1 + CGPS = 1 + Return (JTBF) + } + + // Apply requested state + Switch (ToInteger(GPC)) { + Case (JT_GPC_GSS) { + Printf(" Get current GPU GCx sleep status") + //TODO: include transitions! + If (GTXS(DGPU_RST_N)) { + // GPU powered on + CGCS = 1 + CGPS = 1 + } ElseIf (GTXS(DGPU_PWR_EN)) { + // GPU powered off, GC6 + CGCS = 3 + CGPS = 0 + } Else { + // GPU powered off, D3 cold + CGCS = 2 + CGPS = 0 + } + } + Case (JT_GPC_EGNS) { + Printf(" Enter GC6 without self-refresh") + GC6I() + CPSS = 1 + } + Case (JT_GPC_EGIS) { + Printf(" Enter GC6 with self-refresh") + GC6I() + If (ToInteger(PPC) == 0) { + CPSS = 0 + } + } + Case (JT_GPC_XGXS) { + Printf(" Exit GC6 and stop self-refresh") + GC6O() + + CGCS = 1 + CGPS = 1 + If (ToInteger(PPC) != 0) { + CPSS = 0 + } + } + Case (JT_GPC_XGIS) { + Printf(" Exit GC6 for self-refresh update") + GC6O() + + CGCS = 1 + CGPS = 1 + If (ToInteger(PPC) != 0) { + CPSS = 0 + } + } + Default { + Printf(" Unsupported GPU power control: %o", SFST(GPC)) + } + } + + Return (JTBF) + } + Default { + Printf(" Unsupported function: %o", SFST(Arg0)) + Return (NV_ERROR_UNSUPPORTED) + } + } +} diff --git a/src/drivers/gfx/nvidia/acpi/common/nvpcf.asl b/src/drivers/gfx/nvidia/acpi/common/nvpcf.asl new file mode 100644 index 0000000000..16b9fbd56d --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/common/nvpcf.asl @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define NVPCF_DSM_GUID "36b49710-2483-11e7-9598-0800200c9a66" +#define NVPCF_REVISION_ID 0x00000200 +#define NVPCF_ERROR_SUCCESS 0x0 +#define NVPCF_ERROR_GENERIC 0x80000001 +#define NVPCF_ERROR_UNSUPPORTED 0x80000002 +#define NVPCF_FUNC_GET_SUPPORTED 0x00000000 +#define NVPCF_FUNC_GET_STATIC_CONFIG_TABLES 0x00000001 +#define NVPCF_FUNC_UPDATE_DYNAMIC_PARAMS 0x00000002 + +Name(_HID, "NVDA0820") + +Name(_UID, "NPCF") + +Method(_DSM, 4, Serialized) { + Printf("NVPCF _DSM") + If (Arg0 == ToUUID(NVPCF_DSM_GUID)) { + If (ToInteger(Arg1) == NVPCF_REVISION_ID) { + Return(NPCF(Arg2, Arg3)) + } Else { + Printf(" Unsupported NVPCF revision: %o", SFST(Arg1)) + Return(NVPCF_ERROR_GENERIC) + } + } Else { + Printf(" Unsupported GUID: %o", IDST(Arg0)) + Return(NVPCF_ERROR_GENERIC) + } +} + +Method(NPCF, 2, Serialized) { + Printf(" NVPCF NPCF") + Switch(ToInteger(Arg0)) { + Case(NVPCF_FUNC_GET_SUPPORTED) { + Printf(" Supported Functions") + Return(ITOB( + (1 << NVPCF_FUNC_GET_SUPPORTED) | + (1 << NVPCF_FUNC_GET_STATIC_CONFIG_TABLES) | + (1 << NVPCF_FUNC_UPDATE_DYNAMIC_PARAMS) + )) + } + Case(NVPCF_FUNC_GET_STATIC_CONFIG_TABLES) { + Printf(" Get Static Config") + Return(Buffer(14) { + // Device table header + 0x20, 0x03, 0x01, + // Intel + NVIDIA + 0x00, + // Controller table header + 0x23, 0x04, 0x05, 0x01, + // Dynamic boost controller + 0x01, + // Supports DC + 0x01, + // Reserved + 0x00, 0x00, 0x00, + // Checksum + 0xAD + }) + } + Case(NVPCF_FUNC_UPDATE_DYNAMIC_PARAMS) { + Printf(" Update Dynamic Boost") + + CreateField(Arg1, 0x28, 2, ICMD) // Input command + + Name(PCFP, Buffer(49) { + // Table version + 0x23, + // Table header size + 0x05, + // Size of common status in bytes + 0x10, + // Size of controller entry in bytes + 0x1C, + // Other fields filled in later + }) + CreateByteField(PCFP, 0x04, CCNT) // Controller count + CreateWordField(PCFP, 0x19, ATPP) // AC TPP offset + CreateWordField(PCFP, 0x1D, AMXP) // AC maximum TGP offset + CreateWordField(PCFP, 0x21, AMNP) // AC minimum TGP offset + + Switch(ToInteger(ICMD)) { + Case(0) { + Printf(" Get Controller Params") + // Number of controllers + CCNT = 1 + // AC total processor power offset from default TGP in 1/8 watt units + ATPP = (CONFIG_DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST_TPP << 3) + // AC maximum TGP offset from default TGP in 1/8 watt units + AMXP = (CONFIG_DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST_MAX << 3) + // AC minimum TGP offset from default TGP in 1/8 watt units + AMNP = (CONFIG_DRIVERS_GFX_NVIDIA_DYNAMIC_BOOST_MIN << 3) + Printf("PCFP: %o", SFST(PCFP)) + Return(PCFP) + } + Case(1) { + Printf(" Set Controller Status") + //TODO + Printf("PCFP: %o", SFST(PCFP)) + Return(PCFP) + } + Default { + Printf(" Unknown Input Command: %o", SFST(ICMD)) + Return(NV_ERROR_UNSUPPORTED) + } + } + } + Default { + Printf(" Unsupported function: %o", SFST(Arg0)) + Return(NVPCF_ERROR_UNSUPPORTED) + } + } +} diff --git a/src/drivers/gfx/nvidia/acpi/common/power.asl b/src/drivers/gfx/nvidia/acpi/common/power.asl new file mode 100644 index 0000000000..b285ba6af0 --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/common/power.asl @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +//TODO: evaluate sleeps + +OperationRegion (PCIC, PCI_Config, 0x00, 0xFF) +Field (PCIC, DwordAcc, NoLock, Preserve) { + Offset (0x40), + SSID, 32, // Subsystem vendor and product ID +} + +// Enter GC6 +Method(GC6I, 0, Serialized) { + Printf(" GPU GC6I START") + + // Enter L23 + ^^DL23() + Sleep(5) + + // Put GPU into reset + Printf(" Put GPU into reset") + CTXS(DGPU_RST_N) + Sleep(5) + + Printf(" GPU GC6I FINISH") +} + +// Exit GC6 +Method(GC6O, 0, Serialized) { + Printf(" GPU GC6O START") + + // Bring GPU out of reset + Printf(" Bring GPU out of reset") + STXS(DGPU_RST_N) + Sleep(5) + + // Exit L23 + ^^L23D() + Sleep(5) + + Printf(" GPU GC6O FINISH") +} + +Method (_ON, 0, Serialized) { + Printf(" GPU _ON START") + + If (DFEN == JT_DFGC_DEFER) { + Switch (ToInteger(DFCO)) { + Case (JT_GPC_XGXS) { + Printf(" Exit GC6 and stop self-refresh") + GC6O() + } + Default { + Printf(" Unsupported DFCO: %o", SFST(DFCO)) + } + } + DFEN = JT_DFGC_NONE + } Else { + Printf(" Standard RTD3 power on") + STXS(DGPU_PWR_EN) + Sleep(5) + GC6O() + } + + Printf(" GPU _ON FINISH") +} + +Method (_OFF, 0, Serialized) { + Printf(" GPU _OFF START") + + If (DFEN == JT_DFGC_DEFER) { + Switch (ToInteger(DFCI)) { + Case (JT_GPC_EGNS) { + Printf(" Enter GC6 without self-refresh") + GC6I() + } + Case (JT_GPC_EGIS) { + Printf(" Enter GC6 with self-refresh") + GC6I() + } + Default { + Printf(" Unsupported DFCI: %o", SFST(DFCI)) + } + } + DFEN = JT_DFGC_NONE + } Else { + Printf(" Standard RTD3 power off") + GC6I() + CTXS(DGPU_PWR_EN) + Sleep(5) + } + + Printf(" GPU _OFF FINISH") +} + +// Main power resource +PowerResource (PWRR, 0, 0) { + Name (_STA, 1) + + Method (_ON, 0, Serialized) { + Printf("GPU PWRR._ON") + + // Restore SSID + ^^SSID = DGPU_SSID + Printf(" Restore SSID: %o", SFST(^^SSID)) + + _STA = 1 + } + + Method (_OFF, 0, Serialized) { + Printf("GPU PWRR._OFF") + + _STA = 0 + } +} + +// Power resources for entering D0 +Name (_PR0, Package () { PWRR }) + +// Power resources for entering D3 +Name (_PR3, Package () { PWRR }) diff --git a/src/drivers/gfx/nvidia/acpi/common/utility.asl b/src/drivers/gfx/nvidia/acpi/common/utility.asl new file mode 100644 index 0000000000..edf42bd024 --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/common/utility.asl @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +// Convert a byte to a hex string, trimming extra parts +Method (BHEX, 1) { + Local0 = ToHexString(Arg0) + Return (Mid(Local0, SizeOf(Local0) - 2, 2)) +} + +// UUID to string +Method (IDST, 1) { + Local0 = "" + Fprintf( + Local0, + "%o%o%o%o-%o%o-%o%o-%o%o-%o%o%o%o%o%o", + BHEX(DerefOf(Arg0[3])), + BHEX(DerefOf(Arg0[2])), + BHEX(DerefOf(Arg0[1])), + BHEX(DerefOf(Arg0[0])), + BHEX(DerefOf(Arg0[5])), + BHEX(DerefOf(Arg0[4])), + BHEX(DerefOf(Arg0[7])), + BHEX(DerefOf(Arg0[6])), + BHEX(DerefOf(Arg0[8])), + BHEX(DerefOf(Arg0[9])), + BHEX(DerefOf(Arg0[10])), + BHEX(DerefOf(Arg0[11])), + BHEX(DerefOf(Arg0[12])), + BHEX(DerefOf(Arg0[13])), + BHEX(DerefOf(Arg0[14])), + BHEX(DerefOf(Arg0[15])) + ) + Return (Local0) +} + +// Safe hex conversion, checks type first +Method (SFST, 1) { + Local0 = ObjectType(Arg0) + If (Local0 == 1 || Local0 == 2 || Local0 == 3) { + Return (ToHexString(Arg0)) + } Else { + Return (Concatenate("Type: ", Arg0)) + } +} + +// Convert from 4-byte buffer to 32-bit integer +Method (BTOI, 1) { + Return( + DerefOf(Arg0[0]) | + (DerefOf(Arg0[1]) << 8) | + (DerefOf(Arg0[2]) << 16) | + (DerefOf(Arg0[3]) << 24) + ) +} + +// Convert from 32-bit integer to 4-byte buffer +Method (ITOB, 1) { + Local0 = Buffer(4) { 0, 0, 0, 0 } + Local0[0] = Arg0 & 0xFF + Local0[1] = (Arg0 >> 8) & 0xFF + Local0[2] = (Arg0 >> 16) & 0xFF + Local0[3] = (Arg0 >> 24) & 0xFF + Return (Local0) +} diff --git a/src/drivers/gfx/nvidia/acpi/tigerlake.asl b/src/drivers/gfx/nvidia/acpi/tigerlake.asl new file mode 100644 index 0000000000..a13e46a04f --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/tigerlake.asl @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* NVIDIA GC6 on (TGL and ADL) (CPU and PCH) PCIe ports */ + +// Port mapped PCI express config space +OperationRegion (PCIC, PCI_Config, 0x00, 0xFF) + +Field (PCIC, AnyAcc, NoLock, Preserve) { + Offset(0x52), /* LSTS - Link Status Register */ + , 13, + LASX, 1, /* 0, Link Active Status */ + + Offset(0x60), /* RSTS - Root Status Register */ + , 16, + PSPX, 1, /* 16, PME Status */ + + Offset(0xD8), /* 0xD8, MPC - Miscellaneous Port Configuration Register */ + , 30, + HPEX, 1, /* 30, Hot Plug SCI Enable */ + PMEX, 1, /* 31, Power Management SCI Enable */ + + Offset (0xE0), /* 0xE0, SPR - Scratch Pad Register */ + SCB0, 1, /* Scratch bit 0 */ + + Offset(0xE2), /* 0xE2, RPPGEN - Root Port Power Gating Enable */ + , 2, + L23E, 1, /* 2, L23_Rdy Entry Request (L23ER) */ + L23R, 1, /* 3, L23_Rdy to Detect Transition (L23R2DT) */ +} + +Field (PCIC, AnyAcc, NoLock, WriteAsZeros) { + Offset(0xDC), /* 0xDC, SMSCS - SMI/SCI Status Register */ + , 30, + HPSX, 1, /* 30, Hot Plug SCI Status */ + PMSX, 1 /* 31, Power Management SCI Status */ +} + +// Enter L23 +Method (DL23, 0, Serialized) { + Printf(" GPU PORT DL23 START") + + L23E = 1 + Sleep (16) + Local0 = 0 + While (L23E) { + If ((Local0 > 4)) { + Break + } + + Sleep (16) + Local0++ + } + SCB0 = 1 + + Printf(" GPU PORT DL23 FINISH") +} + +// Exit L23 +Method (L23D, 0, Serialized) { + Printf(" GPU PORT L23D START") + + If ((SCB0 == 1)) { + L23R = 1 + Local0 = 0 + While (L23R) { + If ((Local0 > 4)) { + Break + } + Sleep (16) + Local0++ + } + + SCB0 = 0 + Local0 = 0 + While ((LASX == 0)) { + If ((Local0 > 8)) { + Break + } + Sleep (16) + Local0++ + } + } + + Printf(" GPU PORT L23D FINISH") +} + +Method (HPME, 0, Serialized) { + Printf(" GPU PORT HPME START") + + If (PMSX == 1) { + Printf(" Notify GPU driver of PME SCI") + Notify(DEV0, 0x2) + Printf(" Clear PME SCI") + PMSX = 1 + Printf(" Consume PME notification") + PSPX = 1 + } + + Printf(" GPU PORT HPME FINISH") +} + +// Main power resource +PowerResource (PWRR, 0, 0) { + Name (_STA, 1) + + Method (_ON, 0, Serialized) { + Printf("GPU PORT PWRR._ON") + + HPME(); + If (PMEX == 1) { + Printf(" Disable power management SCI") + PMEX = 0 + } + + ^^DEV0._ON() + + _STA = 1 + } + + Method (_OFF, 0, Serialized) { + Printf("GPU PORT PWRR._OFF") + + ^^DEV0._OFF() + + If (PMEX == 0) { + Printf(" Enable power management SCI") + PMEX = 1 + HPME() + } + + _STA = 0 + } +} + +// Power resources for entering D0 +Name (_PR0, Package () { PWRR }) + +// Power resources for entering D3 +Name (_PR3, Package () { PWRR }) + +#include "common/gpu.asl" diff --git a/src/drivers/gfx/nvidia/chip.h b/src/drivers/gfx/nvidia/chip.h new file mode 100644 index 0000000000..b6d9c908eb --- /dev/null +++ b/src/drivers/gfx/nvidia/chip.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _DRIVERS_GFX_NVIDIA_CHIP_H_ +#define _DRIVERS_GFX_NVIDIA_CHIP_H_ + +struct drivers_gfx_nvidia_config { + /* TODO: Set GPIOs in devicetree? */ +}; + +#endif /* _DRIVERS_GFX_NVIDIA_CHIP_H_ */ diff --git a/src/drivers/gfx/nvidia/gpu.h b/src/drivers/gfx/nvidia/gpu.h new file mode 100644 index 0000000000..68b6cd2d59 --- /dev/null +++ b/src/drivers/gfx/nvidia/gpu.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _DRIVERS_GFX_NVIDIA_GPU_H_ +#define _DRIVERS_GFX_NVIDIA_GPU_H_ + +#include + +struct nvidia_gpu_config { + /* GPIO for GPU_PWR_EN */ + unsigned int power_gpio; + /* GPIO for GPU_RST# */ + unsigned int reset_gpio; + /* Enable or disable GPU power */ + bool enable; +}; + +void nvidia_set_power(const struct nvidia_gpu_config *config); + +#endif /* _DRIVERS_NVIDIA_GPU_H_ */ diff --git a/src/drivers/gfx/nvidia/nvidia.c b/src/drivers/gfx/nvidia/nvidia.c new file mode 100644 index 0000000000..09083e83b5 --- /dev/null +++ b/src/drivers/gfx/nvidia/nvidia.c @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include "chip.h" +#include +#include +#include +#include + +#define NVIDIA_SUBSYSTEM_ID_OFFSET 0x40 + +static void nvidia_read_resources(struct device *dev) +{ + printk(BIOS_DEBUG, "%s: %s\n", __func__, dev_path(dev)); + + pci_dev_read_resources(dev); + + // Find all BARs on GPU, mark them above 4g if prefetchable + for (int bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar += 4) { + struct resource *res = probe_resource(dev, bar); + + if (res) { + if (res->flags & IORESOURCE_PREFETCH) { + printk(BIOS_INFO, " BAR at 0x%02x marked above 4g\n", bar); + res->flags |= IORESOURCE_ABOVE_4G; + } else { + printk(BIOS_DEBUG, " BAR at 0x%02x not prefetch\n", bar); + } + } else { + printk(BIOS_DEBUG, " BAR at 0x%02x not found\n", bar); + } + } +} + +static void nvidia_set_subsystem(struct device *dev, unsigned int vendor, unsigned int device) +{ + pci_write_config32(dev, NVIDIA_SUBSYSTEM_ID_OFFSET, + ((device & 0xffff) << 16) | (vendor & 0xffff)); +} + +static struct pci_operations nvidia_device_ops_pci = { + .set_subsystem = nvidia_set_subsystem, +}; + +static struct device_operations nvidia_device_ops = { + .read_resources = nvidia_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, +#if CONFIG(HAVE_ACPI_TABLES) + .write_acpi_tables = pci_rom_write_acpi_tables, + .acpi_fill_ssdt = pci_rom_ssdt, +#endif + .init = pci_dev_init, + .ops_pci = &nvidia_device_ops_pci, + +}; + +static void nvidia_enable(struct device *dev) +{ + if (!is_dev_enabled(dev) || dev->path.type != DEVICE_PATH_PCI) + return; + + if (pci_read_config16(dev, PCI_VENDOR_ID) != PCI_VID_NVIDIA) + return; + + dev->ops = &nvidia_device_ops; +} + +struct chip_operations drivers_gfx_nvidia_ops = { + .name = "NVIDIA Optimus Graphics Device", + .enable_dev = nvidia_enable +}; diff --git a/src/drivers/gfx/nvidia/romstage.c b/src/drivers/gfx/nvidia/romstage.c new file mode 100644 index 0000000000..78fe5ddf50 --- /dev/null +++ b/src/drivers/gfx/nvidia/romstage.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include "chip.h" +#include "gpu.h" + +void nvidia_set_power(const struct nvidia_gpu_config *config) +{ + if (!config->power_gpio || !config->reset_gpio) { + printk(BIOS_ERR, "%s: GPU_PWR_EN and GPU_RST# must be set\n", __func__); + return; + } + + printk(BIOS_DEBUG, "%s: GPU_PWR_EN = %d\n", __func__, config->power_gpio); + printk(BIOS_DEBUG, "%s: GPU_RST# = %d\n", __func__, config->reset_gpio); + + gpio_set(config->reset_gpio, 0); + mdelay(10); + + if (config->enable) { + gpio_set(config->power_gpio, 1); + mdelay(25); + gpio_set(config->reset_gpio, 1); + } else { + gpio_set(config->power_gpio, 0); + } + + mdelay(10); +}