adjustor 3.3.1__tar.gz → 3.4.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {adjustor-3.3.1/src/adjustor.egg-info → adjustor-3.4.0}/PKG-INFO +51 -8
- {adjustor-3.3.1 → adjustor-3.4.0}/pyproject.toml +1 -1
- {adjustor-3.3.1 → adjustor-3.4.0}/readme.md +50 -7
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/amd/__init__.py +18 -3
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/amd/settings.yml +5 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/asus/__init__.py +179 -79
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/asus/settings.yml +47 -33
- adjustor-3.4.0/src/adjustor/drivers/general/__init__.py +146 -0
- adjustor-3.4.0/src/adjustor/drivers/general/settings.yml +27 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/lenovo/__init__.py +1 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/lenovo/settings.yml +14 -4
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/hhd.py +7 -4
- {adjustor-3.3.1 → adjustor-3.4.0/src/adjustor.egg-info}/PKG-INFO +51 -8
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor.egg-info/SOURCES.txt +2 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/LICENSE +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/MANIFEST.in +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/setup.cfg +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/__init__.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/__main__.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/core/__init__.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/core/acpi.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/core/alib.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/core/const.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/core/lenovo.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/core/platform.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/__init__.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/amd/power-profiles-daemon.dbus.xml.in +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/amd/ppd.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/smu/__init__.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/smu/qam.yml +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/smu/smu.yml +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/events.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/fuse/__init__.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/fuse/driver.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/fuse/gpu.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/fuse/utils.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/i18n.py +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/settings.yml +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor.egg-info/dependency_links.txt +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor.egg-info/entry_points.txt +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor.egg-info/requires.txt +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor.egg-info/top_level.txt +0 -0
- {adjustor-3.3.1 → adjustor-3.4.0}/usr/share/dbus-1/system.d/hhd-net.hadess.PowerProfiles.conf +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: adjustor
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.4.0
|
4
4
|
Summary: Adjustor, a userspace program for managing the TDP of handheld devices.
|
5
5
|
Author-email: Kapenekakis Antheas <pypi@antheas.dev>
|
6
6
|
Project-URL: Homepage, https://github.com/hhd-dev/adjustor
|
@@ -38,7 +38,7 @@ except intel handhelds and older prior to 6XXX AMD handhelds.
|
|
38
38
|
## TDP Control
|
39
39
|
For the ROG Ally, Ally X and Legion Go that have an ACPI/EC implementation for
|
40
40
|
bios and fan curves,
|
41
|
-
Adjustor uses the
|
41
|
+
Adjustor uses the manufacturer functions for setting TDP.
|
42
42
|
For the Allys, the asus-wmi kernel driver is used to set the tdp and manage the
|
43
43
|
fan curves.
|
44
44
|
For the Go, Lenovo's WMI methods are called through `acpi_call`, which will hopefully
|
@@ -53,14 +53,14 @@ For more, see [AMD TDP Control Details](#amd-tdp).
|
|
53
53
|
In all cases, there are checks to ensure that the TDP is within the safe range
|
54
54
|
of the processors.
|
55
55
|
|
56
|
-
## Energy Management
|
57
|
-
Adjustor can also manage the energy profile of the processor, by setting EPP
|
56
|
+
## Energy Management in Handhelds
|
57
|
+
Adjustor can also manage the energy profile of the processor in handhelds, by setting EPP
|
58
58
|
and proper frequency values.
|
59
59
|
After we transitioned people away from Decky plugins (which had some governor controls)
|
60
60
|
to using Handheld Daemon for TDP, we found that Power Profiles Daemon (PPD)
|
61
61
|
would use aggressive CPU values.
|
62
62
|
These values are optimized for devices that have a dedicated power budget for the CPU
|
63
|
-
(e.g., laptops, desktops), which
|
63
|
+
(e.g., laptops, desktops), which causes undesirable behavior handhelds.
|
64
64
|
|
65
65
|
For example, the balanced PPD profile would set EPP to balance_performance and
|
66
66
|
enable CPU boost, which would increase the draw of the CPU during gaming by 2W
|
@@ -91,6 +91,48 @@ TDP range instead of CPU values (which is the user's expectation).
|
|
91
91
|
Of course, depending on TDP and user preference, the CPU governor values will be set
|
92
92
|
accordingly.
|
93
93
|
|
94
|
+
## Energy Management in other computers
|
95
|
+
As we design Handheld Daemon to be enabled in more Deck style devices (e.g., HTPCs),
|
96
|
+
these devices have different power requirements and processors (e.g., Intel), which
|
97
|
+
are better managed with Power Profiles Daemon.
|
98
|
+
It is the aim of the Handheld Daemon project to become a general Deck style session
|
99
|
+
manager for anything gamescope related, with useful features for all devices.
|
100
|
+
|
101
|
+
In these cases, starting with 3.4, for devices that are not in the CPU/device
|
102
|
+
whitelist (includes only AMD U series APUs and handhelds), Adjustor contains a
|
103
|
+
general energy management plugin that allows for switching the PPD power profile
|
104
|
+
from game mode.
|
105
|
+
In addition, it supports [sched_ext schedulers](#sched_ext).
|
106
|
+
|
107
|
+
This means that for general devices, Handheld Daemon uses PPD, and for handhelds,
|
108
|
+
Handheld Daemon becomes PPD.
|
109
|
+
This can be confusing for distribution maintainers and users, as they can and
|
110
|
+
should install both (e.g., Adjustor uses the Power Profile Daemon polkits).
|
111
|
+
|
112
|
+
In any case, Adjustor will never break/conflict with PPD and contains helpful
|
113
|
+
messages about disabling PPD in case the optimized handheld plugin is loaded.
|
114
|
+
For distribution maintainers that ship both and want Handheld Daemon to work
|
115
|
+
without user intervention, the environment variable `HHD_PPD_MASK` is provided.
|
116
|
+
If and only if it is set e.g., by using a systemd service extension, Handheld Daemon
|
117
|
+
will mask and disable PPD if optimized energy management is supported and enabled (e.g., for handhelds).
|
118
|
+
If the general plugin is loaded, it will unmask PPD during startup.
|
119
|
+
This means that Power Management will work properly for all devices without manual
|
120
|
+
intervention or whitelisting by distribution maintainers.
|
121
|
+
|
122
|
+
## Sched_ext<a name="sched-ext"></a>
|
123
|
+
Starting with version 3.3, Adjustor can attach sched_ext schedulers to the
|
124
|
+
kernel if those are supported and installed.
|
125
|
+
Adjustor manages the lifetime of the scheduler, including launching and attaching
|
126
|
+
it, without using a systemd service, and is fully responsive to quirk scheduler
|
127
|
+
switches.
|
128
|
+
|
129
|
+
Schedulers are whitelisted in a case by case basis, with LAVD, rusty, and bpfland
|
130
|
+
being supported in the current version (the `scx_` namespace is crowded with e.g.,
|
131
|
+
test schedulers).
|
132
|
+
Of course, only installed schedulers are shown if and only if the kernel supports
|
133
|
+
them.
|
134
|
+
Get in touch to add your favorite scheduler, as it is a single line change.
|
135
|
+
|
94
136
|
## AMD TDP Control Details<a name="amd-tdp"></a>
|
95
137
|
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
96
138
|
of AMD, which exposes a superset of the parameters that can be currently found in
|
@@ -99,7 +141,7 @@ This vendor interface is part of the ACPI ASL library, and provided through the
|
|
99
141
|
ALIB method 0x0C.
|
100
142
|
The underlying implementation of the interface is SMU calls.
|
101
143
|
This means that as long as the kernel module `acpi_call` is loaded, Adjustor
|
102
|
-
can control TDP in
|
144
|
+
can control TDP in a similar way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
|
103
145
|
|
104
146
|
The ABI of this vendor function (as it is provided to manufacturers) can be
|
105
147
|
considered mostly stable, so little work is needed between subsequent
|
@@ -109,8 +151,9 @@ Of course, support for processors is only added after the ACPI bindings have
|
|
109
151
|
been reviewed, to avoid surprises.
|
110
152
|
Both the Ally and Legion Go use this function, in the exact same way, so setting
|
111
153
|
TDP with it is very stable, and we have had no reported crashes.
|
112
|
-
It
|
113
|
-
manufacturer functions
|
154
|
+
It can not be used and is not used with those devices, however, as the
|
155
|
+
manufacturer functions would interfere and provide a better user experience,
|
156
|
+
such as setting appropriate fan curves and changing the power light color on the Legion Go.
|
114
157
|
|
115
158
|
Unfortunately for devices that do have an ACPI/EC implementation for TDP, there
|
116
159
|
is no official way of setting TDP on demand, either on Linux or Windows, with
|
@@ -17,7 +17,7 @@ except intel handhelds and older prior to 6XXX AMD handhelds.
|
|
17
17
|
## TDP Control
|
18
18
|
For the ROG Ally, Ally X and Legion Go that have an ACPI/EC implementation for
|
19
19
|
bios and fan curves,
|
20
|
-
Adjustor uses the
|
20
|
+
Adjustor uses the manufacturer functions for setting TDP.
|
21
21
|
For the Allys, the asus-wmi kernel driver is used to set the tdp and manage the
|
22
22
|
fan curves.
|
23
23
|
For the Go, Lenovo's WMI methods are called through `acpi_call`, which will hopefully
|
@@ -32,14 +32,14 @@ For more, see [AMD TDP Control Details](#amd-tdp).
|
|
32
32
|
In all cases, there are checks to ensure that the TDP is within the safe range
|
33
33
|
of the processors.
|
34
34
|
|
35
|
-
## Energy Management
|
36
|
-
Adjustor can also manage the energy profile of the processor, by setting EPP
|
35
|
+
## Energy Management in Handhelds
|
36
|
+
Adjustor can also manage the energy profile of the processor in handhelds, by setting EPP
|
37
37
|
and proper frequency values.
|
38
38
|
After we transitioned people away from Decky plugins (which had some governor controls)
|
39
39
|
to using Handheld Daemon for TDP, we found that Power Profiles Daemon (PPD)
|
40
40
|
would use aggressive CPU values.
|
41
41
|
These values are optimized for devices that have a dedicated power budget for the CPU
|
42
|
-
(e.g., laptops, desktops), which
|
42
|
+
(e.g., laptops, desktops), which causes undesirable behavior handhelds.
|
43
43
|
|
44
44
|
For example, the balanced PPD profile would set EPP to balance_performance and
|
45
45
|
enable CPU boost, which would increase the draw of the CPU during gaming by 2W
|
@@ -70,6 +70,48 @@ TDP range instead of CPU values (which is the user's expectation).
|
|
70
70
|
Of course, depending on TDP and user preference, the CPU governor values will be set
|
71
71
|
accordingly.
|
72
72
|
|
73
|
+
## Energy Management in other computers
|
74
|
+
As we design Handheld Daemon to be enabled in more Deck style devices (e.g., HTPCs),
|
75
|
+
these devices have different power requirements and processors (e.g., Intel), which
|
76
|
+
are better managed with Power Profiles Daemon.
|
77
|
+
It is the aim of the Handheld Daemon project to become a general Deck style session
|
78
|
+
manager for anything gamescope related, with useful features for all devices.
|
79
|
+
|
80
|
+
In these cases, starting with 3.4, for devices that are not in the CPU/device
|
81
|
+
whitelist (includes only AMD U series APUs and handhelds), Adjustor contains a
|
82
|
+
general energy management plugin that allows for switching the PPD power profile
|
83
|
+
from game mode.
|
84
|
+
In addition, it supports [sched_ext schedulers](#sched_ext).
|
85
|
+
|
86
|
+
This means that for general devices, Handheld Daemon uses PPD, and for handhelds,
|
87
|
+
Handheld Daemon becomes PPD.
|
88
|
+
This can be confusing for distribution maintainers and users, as they can and
|
89
|
+
should install both (e.g., Adjustor uses the Power Profile Daemon polkits).
|
90
|
+
|
91
|
+
In any case, Adjustor will never break/conflict with PPD and contains helpful
|
92
|
+
messages about disabling PPD in case the optimized handheld plugin is loaded.
|
93
|
+
For distribution maintainers that ship both and want Handheld Daemon to work
|
94
|
+
without user intervention, the environment variable `HHD_PPD_MASK` is provided.
|
95
|
+
If and only if it is set e.g., by using a systemd service extension, Handheld Daemon
|
96
|
+
will mask and disable PPD if optimized energy management is supported and enabled (e.g., for handhelds).
|
97
|
+
If the general plugin is loaded, it will unmask PPD during startup.
|
98
|
+
This means that Power Management will work properly for all devices without manual
|
99
|
+
intervention or whitelisting by distribution maintainers.
|
100
|
+
|
101
|
+
## Sched_ext<a name="sched-ext"></a>
|
102
|
+
Starting with version 3.3, Adjustor can attach sched_ext schedulers to the
|
103
|
+
kernel if those are supported and installed.
|
104
|
+
Adjustor manages the lifetime of the scheduler, including launching and attaching
|
105
|
+
it, without using a systemd service, and is fully responsive to quirk scheduler
|
106
|
+
switches.
|
107
|
+
|
108
|
+
Schedulers are whitelisted in a case by case basis, with LAVD, rusty, and bpfland
|
109
|
+
being supported in the current version (the `scx_` namespace is crowded with e.g.,
|
110
|
+
test schedulers).
|
111
|
+
Of course, only installed schedulers are shown if and only if the kernel supports
|
112
|
+
them.
|
113
|
+
Get in touch to add your favorite scheduler, as it is a single line change.
|
114
|
+
|
73
115
|
## AMD TDP Control Details<a name="amd-tdp"></a>
|
74
116
|
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
75
117
|
of AMD, which exposes a superset of the parameters that can be currently found in
|
@@ -78,7 +120,7 @@ This vendor interface is part of the ACPI ASL library, and provided through the
|
|
78
120
|
ALIB method 0x0C.
|
79
121
|
The underlying implementation of the interface is SMU calls.
|
80
122
|
This means that as long as the kernel module `acpi_call` is loaded, Adjustor
|
81
|
-
can control TDP in
|
123
|
+
can control TDP in a similar way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
|
82
124
|
|
83
125
|
The ABI of this vendor function (as it is provided to manufacturers) can be
|
84
126
|
considered mostly stable, so little work is needed between subsequent
|
@@ -88,8 +130,9 @@ Of course, support for processors is only added after the ACPI bindings have
|
|
88
130
|
been reviewed, to avoid surprises.
|
89
131
|
Both the Ally and Legion Go use this function, in the exact same way, so setting
|
90
132
|
TDP with it is very stable, and we have had no reported crashes.
|
91
|
-
It
|
92
|
-
manufacturer functions
|
133
|
+
It can not be used and is not used with those devices, however, as the
|
134
|
+
manufacturer functions would interfere and provide a better user experience,
|
135
|
+
such as setting appropriate fan curves and changing the power light color on the Legion Go.
|
93
136
|
|
94
137
|
Unfortunately for devices that do have an ACPI/EC implementation for TDP, there
|
95
138
|
is no official way of setting TDP on demand, either on Linux or Windows, with
|
@@ -24,7 +24,7 @@ from adjustor.fuse.gpu import (
|
|
24
24
|
|
25
25
|
logger = logging.getLogger(__name__)
|
26
26
|
|
27
|
-
APPLY_DELAY = 0.
|
27
|
+
APPLY_DELAY = 0.2
|
28
28
|
|
29
29
|
|
30
30
|
def _ppd_client(emit, proc):
|
@@ -126,14 +126,29 @@ class AmdGPUPlugin(HHDPlugin):
|
|
126
126
|
]
|
127
127
|
)
|
128
128
|
for line in out.decode().splitlines():
|
129
|
-
|
129
|
+
line = line.lower()
|
130
|
+
if "masked" in line:
|
130
131
|
continue
|
131
|
-
if "
|
132
|
+
if "not-found" in line:
|
133
|
+
continue
|
134
|
+
if "inactive" in line:
|
135
|
+
continue
|
136
|
+
if "power-profiles-daemon" in line or "tuned" in line:
|
132
137
|
self.ppd_conflict = True
|
133
138
|
break
|
134
139
|
except Exception as e:
|
135
140
|
logger.error(f"Failed to check for PPD conflict:\n{e}")
|
136
141
|
|
142
|
+
if self.ppd_conflict and os.environ.get("HHD_PPD_MASK", None):
|
143
|
+
logger.warning(
|
144
|
+
"PPD conflict detected but HHD_PPD_MASK is set. Masking PPD."
|
145
|
+
)
|
146
|
+
# Mask and disable
|
147
|
+
os.system("systemctl mask power-profiles-daemon.service")
|
148
|
+
os.system("systemctl disable --now power-profiles-daemon.service")
|
149
|
+
# Keep going without check to avoid obscure errors
|
150
|
+
self.ppd_conflict = False
|
151
|
+
|
137
152
|
if self.ppd_conflict:
|
138
153
|
self.initialized = False
|
139
154
|
return {
|
@@ -46,6 +46,11 @@ enabled:
|
|
46
46
|
options:
|
47
47
|
min: 400MHz
|
48
48
|
nonlinear: 1GHz
|
49
|
+
hint: >-
|
50
|
+
Sets the minimum frequency for the CPU.
|
51
|
+
Using 400MHz will save battery in light games.
|
52
|
+
However, the delay of increasing the frequency
|
53
|
+
may cause minor stutters, especially in VRR displays.
|
49
54
|
cpu_boost:
|
50
55
|
type: multiple
|
51
56
|
title: CPU Boost
|
@@ -9,8 +9,8 @@ from adjustor.core.platform import set_platform_profile
|
|
9
9
|
|
10
10
|
logger = logging.getLogger(__name__)
|
11
11
|
|
12
|
-
APPLY_DELAY =
|
13
|
-
TDP_DELAY = 0.
|
12
|
+
APPLY_DELAY = 0.7
|
13
|
+
TDP_DELAY = 0.1
|
14
14
|
MIN_TDP_START = 7
|
15
15
|
MAX_TDP_START = 30
|
16
16
|
# FIXME: add AC/DC values
|
@@ -123,7 +123,7 @@ def disable_fan_curve():
|
|
123
123
|
|
124
124
|
|
125
125
|
class AsusDriverPlugin(HHDPlugin):
|
126
|
-
def __init__(self) -> None:
|
126
|
+
def __init__(self, allyx: bool = False) -> None:
|
127
127
|
self.name = f"adjustor_asus"
|
128
128
|
self.priority = 6
|
129
129
|
self.log = "adja"
|
@@ -132,11 +132,16 @@ class AsusDriverPlugin(HHDPlugin):
|
|
132
132
|
self.enforce_limits = True
|
133
133
|
self.startup = True
|
134
134
|
self.old_conf = None
|
135
|
+
self.mode = None
|
136
|
+
self.cycle_tdp = None
|
135
137
|
|
136
138
|
self.queue_fan = None
|
137
139
|
self.queue_tdp = None
|
138
140
|
self.new_tdp = None
|
141
|
+
self.new_mode = None
|
139
142
|
self.old_target = None
|
143
|
+
self.pp = None
|
144
|
+
self.allyx = allyx
|
140
145
|
|
141
146
|
def settings(self):
|
142
147
|
if not self.enabled:
|
@@ -147,8 +152,25 @@ class AsusDriverPlugin(HHDPlugin):
|
|
147
152
|
|
148
153
|
self.initialized = True
|
149
154
|
out = {"tdp": {"asus": load_relative_yaml("settings.yml")}}
|
155
|
+
|
156
|
+
# Set units
|
157
|
+
if self.allyx:
|
158
|
+
out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["quiet"]["unit"] = "13W"
|
159
|
+
out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["balanced"]["unit"] = "17W"
|
160
|
+
out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["performance"][
|
161
|
+
"unit"
|
162
|
+
] = "25W"
|
163
|
+
else:
|
164
|
+
out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["quiet"]["unit"] = "10W"
|
165
|
+
out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["balanced"]["unit"] = "15W"
|
166
|
+
out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["performance"][
|
167
|
+
"unit"
|
168
|
+
] = "20W"
|
169
|
+
|
150
170
|
if not self.enforce_limits:
|
151
|
-
out["tdp"]["asus"]["children"]["
|
171
|
+
out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["custom"]["children"]["tdp"][
|
172
|
+
"max"
|
173
|
+
] = 50
|
152
174
|
return out
|
153
175
|
|
154
176
|
def open(
|
@@ -204,78 +226,120 @@ class AsusDriverPlugin(HHDPlugin):
|
|
204
226
|
# TDP
|
205
227
|
#
|
206
228
|
new_target = None
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
if
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
229
|
+
new_tdp = self.new_tdp
|
230
|
+
self.new_tdp = None
|
231
|
+
new_mode = self.new_mode
|
232
|
+
self.new_mode = None
|
233
|
+
ally_x = self.allyx
|
234
|
+
if new_tdp:
|
235
|
+
# For TDP values received from steam, set the appropriate
|
236
|
+
# mode to get a better experience.
|
237
|
+
if new_tdp == (13 if ally_x else 10):
|
238
|
+
mode = "quiet"
|
239
|
+
elif new_tdp == (17 if ally_x else 15):
|
240
|
+
mode = "balanced"
|
241
|
+
elif new_tdp == 25 or new_tdp == 30:
|
242
|
+
mode = "performance"
|
243
|
+
else:
|
244
|
+
mode = "custom"
|
245
|
+
conf["tdp.asus.tdp_v2.mode"] = mode
|
246
|
+
elif new_mode:
|
247
|
+
mode = new_mode
|
248
|
+
conf["tdp.asus.tdp_v2.mode"] = mode
|
220
249
|
else:
|
221
|
-
|
222
|
-
|
223
|
-
|
250
|
+
mode = conf["tdp.asus.tdp_v2.mode"].to(str)
|
251
|
+
self.mode = mode
|
252
|
+
|
253
|
+
tdp_reset = False
|
254
|
+
if mode is not None and mode != self.old_conf["tdp_v2.mode"].to(str):
|
255
|
+
tdp_reset = True
|
256
|
+
|
257
|
+
# Handle EPP for presets
|
258
|
+
if tdp_reset and mode != "custom":
|
259
|
+
match mode:
|
260
|
+
case "quiet":
|
261
|
+
set_platform_profile("quiet")
|
262
|
+
new_target = "power"
|
263
|
+
case "balanced":
|
264
|
+
set_platform_profile("balanced")
|
265
|
+
new_target = "balanced"
|
266
|
+
case _: # "performance":
|
267
|
+
set_platform_profile("performance")
|
268
|
+
new_target = "performance"
|
269
|
+
|
270
|
+
# In custom mode, re-apply settings with debounce
|
271
|
+
if mode == "custom":
|
272
|
+
# Check user changed values
|
273
|
+
if new_tdp:
|
274
|
+
steady = new_tdp
|
275
|
+
conf["tdp.asus.tdp_v2.custom.tdp"] = steady
|
276
|
+
else:
|
277
|
+
steady = conf["tdp.asus.tdp_v2.custom.tdp"].to(int)
|
224
278
|
|
225
|
-
|
226
|
-
|
227
|
-
f"TDP ({steady}) outside the device spec. Resetting for stability reasons."
|
279
|
+
steady_updated = steady and steady != self.old_conf["tdp_v2.custom.tdp"].to(
|
280
|
+
int
|
228
281
|
)
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
boost_updated = boost != self.old_conf["boost"].to(bool)
|
235
|
-
|
236
|
-
# If yes, queue an update
|
237
|
-
# Debounce
|
238
|
-
if self.startup or steady_updated or boost_updated:
|
239
|
-
self.queue_tdp = curr + APPLY_DELAY
|
240
|
-
|
241
|
-
tdp_set = self.queue_tdp and self.queue_tdp < curr
|
242
|
-
if tdp_set:
|
243
|
-
if steady < 5:
|
244
|
-
steady = 5
|
245
|
-
if steady < 13:
|
246
|
-
set_platform_profile("quiet")
|
247
|
-
new_target = "power"
|
248
|
-
elif steady < 20:
|
249
|
-
set_platform_profile("balanced")
|
250
|
-
new_target = "balanced"
|
251
|
-
else:
|
252
|
-
set_platform_profile("performance")
|
253
|
-
new_target = "performance"
|
254
|
-
|
255
|
-
self.queue_tdp = None
|
256
|
-
if boost:
|
257
|
-
# TODO: Use different boost values depending on whether plugged in
|
258
|
-
time.sleep(TDP_DELAY)
|
259
|
-
set_tdp(
|
260
|
-
"fast", FTDP_FN, min(max(steady, MAX_TDP), int(steady * 35 / 25))
|
261
|
-
)
|
262
|
-
time.sleep(TDP_DELAY)
|
263
|
-
set_tdp(
|
264
|
-
"slow", STDP_FN, min(max(steady, MAX_TDP), int(steady * 30 / 25))
|
282
|
+
steady_updated |= tdp_reset
|
283
|
+
|
284
|
+
if self.startup and (steady > MAX_TDP_START or steady < MIN_TDP_START):
|
285
|
+
logger.warning(
|
286
|
+
f"TDP ({steady}) outside the device spec. Resetting for stability reasons."
|
265
287
|
)
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
288
|
+
steady = min(max(steady, MIN_TDP_START), MAX_TDP_START)
|
289
|
+
conf["tdp.asus.tdp_v2.custom.tdp"] = steady
|
290
|
+
steady_updated = True
|
291
|
+
|
292
|
+
boost = conf["tdp.asus.tdp_v2.custom.boost"].to(bool)
|
293
|
+
boost_updated = boost != self.old_conf["tdp_v2.custom.boost"].to(bool)
|
294
|
+
|
295
|
+
# If yes, queue an update
|
296
|
+
# Debounce
|
297
|
+
if self.startup or steady_updated or boost_updated:
|
298
|
+
self.queue_tdp = curr + APPLY_DELAY
|
299
|
+
|
300
|
+
tdp_set = self.queue_tdp and self.queue_tdp < curr
|
301
|
+
if tdp_set:
|
302
|
+
if steady < 5:
|
303
|
+
steady = 5
|
304
|
+
if steady < (15 if self.allyx else 13):
|
305
|
+
set_platform_profile("quiet")
|
306
|
+
new_target = "power"
|
307
|
+
elif steady < (22 if self.allyx else 20):
|
308
|
+
set_platform_profile("balanced")
|
309
|
+
new_target = "balanced"
|
310
|
+
else:
|
311
|
+
set_platform_profile("performance")
|
312
|
+
new_target = "performance"
|
313
|
+
|
314
|
+
self.queue_tdp = None
|
315
|
+
if boost:
|
316
|
+
# TODO: Use different boost values depending on whether plugged in
|
317
|
+
time.sleep(TDP_DELAY)
|
318
|
+
set_tdp(
|
319
|
+
"fast",
|
320
|
+
FTDP_FN,
|
321
|
+
min(max(steady, MAX_TDP), int(steady * 35 / 25)),
|
322
|
+
)
|
323
|
+
time.sleep(TDP_DELAY)
|
324
|
+
set_tdp(
|
325
|
+
"slow",
|
326
|
+
STDP_FN,
|
327
|
+
min(max(steady, MAX_TDP), int(steady * 30 / 25)),
|
328
|
+
)
|
329
|
+
time.sleep(TDP_DELAY)
|
330
|
+
set_tdp("steady", CTDP_FN, steady)
|
331
|
+
else:
|
332
|
+
time.sleep(TDP_DELAY)
|
333
|
+
set_tdp("fast", FTDP_FN, steady)
|
334
|
+
time.sleep(TDP_DELAY)
|
335
|
+
set_tdp("slow", STDP_FN, steady)
|
336
|
+
time.sleep(TDP_DELAY)
|
337
|
+
set_tdp("steady", CTDP_FN, steady)
|
275
338
|
|
276
339
|
if new_target and new_target != self.old_target:
|
277
340
|
self.old_target = new_target
|
278
341
|
self.emit({"type": "energy", "status": new_target})
|
342
|
+
self.pp = new_target
|
279
343
|
|
280
344
|
# Handle fan curve resets
|
281
345
|
if conf["tdp.asus.fan.manual.reset"].to(bool):
|
@@ -283,16 +347,31 @@ class AsusDriverPlugin(HHDPlugin):
|
|
283
347
|
for k, v in zip(POINTS, DEFAULT_CURVE):
|
284
348
|
conf[f"tdp.asus.fan.manual.st{k}"] = v
|
285
349
|
|
286
|
-
|
287
|
-
|
288
|
-
#
|
289
|
-
#
|
290
|
-
#
|
350
|
+
manual_fan_curve = conf["tdp.asus.fan.mode"].to(str) == "manual"
|
351
|
+
|
352
|
+
# Handle fan curve limits by Asus
|
353
|
+
# by enforcing minimum values based on power profile
|
354
|
+
# which is a proxy of the current platform profile but still
|
355
|
+
# a bit of a hack. TODO: Get the exact limits.
|
356
|
+
# FIXME: Revisit limits
|
357
|
+
# if manual_fan_curve:
|
358
|
+
# match self.pp:
|
359
|
+
# case "balanced":
|
360
|
+
# min_val = 45
|
361
|
+
# case "performance":
|
362
|
+
# min_val = 60
|
363
|
+
# case _: # quiet
|
364
|
+
# min_val = 17
|
365
|
+
|
366
|
+
# for k in POINTS:
|
367
|
+
# if conf[f"tdp.asus.fan.manual.st{k}"].to(int) < min_val:
|
368
|
+
# conf[f"tdp.asus.fan.manual.st{k}"] = min_val
|
291
369
|
|
292
370
|
# Check if fan curve has changed
|
293
371
|
# Use debounce logic on these changes
|
294
|
-
if
|
372
|
+
if tdp_reset and manual_fan_curve:
|
295
373
|
self.queue_fan = curr + APPLY_DELAY
|
374
|
+
|
296
375
|
for i in POINTS:
|
297
376
|
if conf[f"tdp.asus.fan.manual.st{i}"].to(int) != self.old_conf[
|
298
377
|
f"fan.manual.st{i}"
|
@@ -329,6 +408,7 @@ class AsusDriverPlugin(HHDPlugin):
|
|
329
408
|
self.queue_fan = None
|
330
409
|
|
331
410
|
# Save current config
|
411
|
+
self.cycle_tdp = conf["tdp.asus.cycle_tdp"].to(bool)
|
332
412
|
self.old_conf = conf["tdp.asus"]
|
333
413
|
|
334
414
|
if self.startup:
|
@@ -338,15 +418,35 @@ class AsusDriverPlugin(HHDPlugin):
|
|
338
418
|
for ev in events:
|
339
419
|
if ev["type"] == "tdp":
|
340
420
|
self.new_tdp = ev["tdp"]
|
341
|
-
|
342
|
-
# TODO: Replace with power modes after refactor
|
421
|
+
elif ev["type"] == "ppd":
|
343
422
|
match ev["status"]:
|
344
423
|
case "power":
|
345
|
-
self.
|
424
|
+
self.new_mode = "quiet"
|
425
|
+
case "balanced":
|
426
|
+
self.new_mode = "balanced"
|
427
|
+
case "performance":
|
428
|
+
self.new_mode = "performance"
|
429
|
+
elif self.cycle_tdp and ev['type'] == "special" and ev['event'] == "xbox_y":
|
430
|
+
match self.mode:
|
431
|
+
case "quiet":
|
432
|
+
self.new_mode = "balanced"
|
433
|
+
event = "tdp_cycle_balanced"
|
346
434
|
case "balanced":
|
347
|
-
self.
|
435
|
+
self.new_mode = "performance"
|
436
|
+
event = "tdp_cycle_performance"
|
348
437
|
case "performance":
|
349
|
-
self.
|
438
|
+
self.new_mode = "custom"
|
439
|
+
event = "tdp_cycle_custom"
|
440
|
+
case "custom":
|
441
|
+
self.new_mode = "quiet"
|
442
|
+
event = "tdp_cycle_quiet"
|
443
|
+
case _:
|
444
|
+
self.new_mode = "balanced"
|
445
|
+
event = "tdp_cycle_balanced"
|
446
|
+
|
447
|
+
logger.info(f"Cycling TDP to '{self.new_mode}'")
|
448
|
+
if self.emit:
|
449
|
+
self.emit({"type": "special", "event": event})
|
350
450
|
|
351
451
|
def close(self):
|
352
452
|
pass
|
@@ -4,27 +4,54 @@ tags: [hide-title]
|
|
4
4
|
hint: >-
|
5
5
|
Uses the interface of Armory Crate to set the TDP of the device.
|
6
6
|
children:
|
7
|
-
|
8
|
-
type:
|
9
|
-
title: TDP
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
tdp_v2:
|
8
|
+
type: mode
|
9
|
+
title: TDP Mode
|
10
|
+
default: balanced
|
11
|
+
modes:
|
12
|
+
quiet:
|
13
|
+
type: container
|
14
|
+
title: Silent
|
15
|
+
unit:
|
16
|
+
balanced:
|
17
|
+
type: container
|
18
|
+
title: Performance
|
19
|
+
unit:
|
20
|
+
performance:
|
21
|
+
type: container
|
22
|
+
title: Turbo
|
23
|
+
unit:
|
24
|
+
custom:
|
25
|
+
type: container
|
26
|
+
title: Custom
|
27
|
+
unit: → 25/30W
|
28
|
+
children:
|
29
|
+
tdp:
|
30
|
+
type: int
|
31
|
+
title: TDP
|
32
|
+
hint: >-
|
33
|
+
Average TDP Target.
|
34
|
+
TDP Boost is recommended for desktop use and does not affect gaming.
|
16
35
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
36
|
+
min: 0
|
37
|
+
max: 30
|
38
|
+
step: 1
|
39
|
+
default: 30
|
40
|
+
unit: W
|
41
|
+
boost:
|
42
|
+
type: bool
|
43
|
+
title: TDP Boost
|
44
|
+
default: True
|
45
|
+
hint: >-
|
46
|
+
Allows the device to temporarily boost by setting appropriate slow and fast TDPs.
|
47
|
+
|
48
|
+
cycle_tdp:
|
23
49
|
type: bool
|
24
|
-
title: TDP
|
25
|
-
default: True
|
50
|
+
title: Change TDP with View+Y
|
26
51
|
hint: >-
|
27
|
-
Allows
|
52
|
+
Allows you to cycle through TDP modes with the View+Y key combination.
|
53
|
+
Recommended to use with ROG Swap, as the View button will be muted to games.
|
54
|
+
default: False
|
28
55
|
|
29
56
|
fan:
|
30
57
|
type: mode
|
@@ -94,8 +121,7 @@ children:
|
|
94
121
|
title: Fan Curve Limitation
|
95
122
|
type: display
|
96
123
|
default: >-
|
97
|
-
Asus hardware limits the minimum fan curve depending on TDP.
|
98
|
-
The minimums are 25%, 50%, and 75% for low, medium, and high TDPs.
|
124
|
+
Asus hardware limits the minimum fan curve depending on TDP mode.
|
99
125
|
|
100
126
|
charge_limit:
|
101
127
|
type: multiple
|
@@ -110,16 +136,4 @@ children:
|
|
110
136
|
p90: 90%
|
111
137
|
p95: 95%
|
112
138
|
disabled: Unset
|
113
|
-
default: disabled
|
114
|
-
|
115
|
-
# disclaimer:
|
116
|
-
# title: Sleep Bug
|
117
|
-
# type: display
|
118
|
-
# tags: [ non-essential ]
|
119
|
-
# default: >-
|
120
|
-
# There is an Asus kernel/BIOS bug that will sometimes limit TDP to 10W
|
121
|
-
# after sleep.
|
122
|
-
# As the kernel driver is used, this is unfixable from within
|
123
|
-
# Handheld Daemon (currently investigated).
|
124
|
-
# As an alternative, use SimpleDeckyTDP with RyzenAdj after
|
125
|
-
# disabling TDP controls from "Settings".
|
139
|
+
default: disabled
|
@@ -0,0 +1,146 @@
|
|
1
|
+
import os
|
2
|
+
import subprocess
|
3
|
+
from typing import Literal
|
4
|
+
import shutil
|
5
|
+
|
6
|
+
import time
|
7
|
+
import signal
|
8
|
+
from hhd.plugins import Context, HHDPlugin, load_relative_yaml
|
9
|
+
from hhd.plugins.conf import Config
|
10
|
+
import logging
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
class GeneralPowerPlugin(HHDPlugin):
|
16
|
+
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
) -> None:
|
20
|
+
self.name = f"adjustor_general"
|
21
|
+
self.priority = 8
|
22
|
+
self.log = "gpow"
|
23
|
+
self.last_check = None
|
24
|
+
self.target = None
|
25
|
+
self.old_sched = None
|
26
|
+
self.sched_proc = None
|
27
|
+
self.ppd_supported = None
|
28
|
+
|
29
|
+
def settings(self):
|
30
|
+
sets = load_relative_yaml("./settings.yml")
|
31
|
+
|
32
|
+
# PPD
|
33
|
+
if self.ppd_supported is None:
|
34
|
+
self.ppd_supported = False
|
35
|
+
if ppc := shutil.which('powerprofilesctl'):
|
36
|
+
try:
|
37
|
+
if os.environ.get("HHD_PPD_MASK", None):
|
38
|
+
logger.info("Unmasking Power Profiles Daemon in the case it was masked.")
|
39
|
+
os.system('systemctl unmask power-profiles-daemon')
|
40
|
+
subprocess.run(
|
41
|
+
[ppc],
|
42
|
+
check=True,
|
43
|
+
stdin=subprocess.DEVNULL,
|
44
|
+
stdout=subprocess.DEVNULL,
|
45
|
+
stderr=subprocess.DEVNULL,
|
46
|
+
)
|
47
|
+
self.ppd_supported = True
|
48
|
+
except Exception as e:
|
49
|
+
logger.warning(f"powerprofilectl returned with error:\n{e}")
|
50
|
+
|
51
|
+
if not self.ppd_supported:
|
52
|
+
del sets["children"]["profile"]
|
53
|
+
|
54
|
+
# SchedExt
|
55
|
+
self.avail_scheds = {}
|
56
|
+
avail_pretty = {}
|
57
|
+
kernel_supports = os.path.isfile("/sys/kernel/sched_ext/state")
|
58
|
+
if kernel_supports:
|
59
|
+
for sched, pretty in sets["children"]["sched"]["options"].items():
|
60
|
+
if sched == "disabled":
|
61
|
+
avail_pretty[sched] = pretty
|
62
|
+
continue
|
63
|
+
|
64
|
+
exe = shutil.which(sched)
|
65
|
+
if exe:
|
66
|
+
self.avail_scheds[sched] = exe
|
67
|
+
avail_pretty[sched] = pretty
|
68
|
+
|
69
|
+
if self.avail_scheds:
|
70
|
+
sets["children"]["sched"]["options"] = avail_pretty
|
71
|
+
else:
|
72
|
+
del sets["children"]["sched"]
|
73
|
+
|
74
|
+
self.logged_boost = True
|
75
|
+
return {
|
76
|
+
"tdp": {"general": sets},
|
77
|
+
}
|
78
|
+
|
79
|
+
def update(self, conf: Config):
|
80
|
+
# Handle ppd
|
81
|
+
if self.ppd_supported:
|
82
|
+
curr = time.time()
|
83
|
+
new_profile = conf.get("tdp.general.profile", self.target)
|
84
|
+
if new_profile != self.target and new_profile and self.target:
|
85
|
+
logger.info(f"Setting power profile to '{new_profile}'")
|
86
|
+
self.target = new_profile
|
87
|
+
try:
|
88
|
+
subprocess.run(
|
89
|
+
[shutil.which('powerprofilesctl'), "set", new_profile],
|
90
|
+
check=True,
|
91
|
+
stdout=subprocess.PIPE,
|
92
|
+
stderr=subprocess.PIPE,
|
93
|
+
)
|
94
|
+
except Exception as e:
|
95
|
+
self.ppd_supported = False
|
96
|
+
logger.warning(f"powerprofilesctl returned with error:\n{e}")
|
97
|
+
self.ppd_supported = False
|
98
|
+
elif not self.last_check or curr - self.last_check > 2:
|
99
|
+
# Update profile every 2 seconds
|
100
|
+
self.last_check = curr
|
101
|
+
try:
|
102
|
+
res = subprocess.run(
|
103
|
+
[shutil.which('powerprofilesctl'), "get"],
|
104
|
+
check=True,
|
105
|
+
stdout=subprocess.PIPE,
|
106
|
+
stderr=subprocess.PIPE,
|
107
|
+
)
|
108
|
+
self.target = res.stdout.decode().strip() # type: ignore
|
109
|
+
if self.target != conf["tdp.general.profile"].to(str):
|
110
|
+
conf["tdp.general.profile"] = self.target
|
111
|
+
except Exception as e:
|
112
|
+
self.ppd_supported = False
|
113
|
+
logger.warning(f"powerprofilectl returned with error:\n{e}")
|
114
|
+
self.ppd_supported = False
|
115
|
+
|
116
|
+
# Handle sched
|
117
|
+
if self.avail_scheds:
|
118
|
+
# Check health and print error
|
119
|
+
if self.sched_proc and self.sched_proc.poll():
|
120
|
+
err = self.sched_proc.poll()
|
121
|
+
self.sched_proc = None
|
122
|
+
logger.error(
|
123
|
+
f"Scheduler from sched_ext '{self.old_sched}' closed with error code: {err}"
|
124
|
+
)
|
125
|
+
|
126
|
+
new_sched = conf.get("tdp.general.sched", "disabled")
|
127
|
+
if new_sched != self.old_sched:
|
128
|
+
self.close_sched()
|
129
|
+
self.old_sched = new_sched
|
130
|
+
if new_sched and new_sched != "disabled":
|
131
|
+
logger.info(f"Starting sched_ext scheduler '{new_sched}'")
|
132
|
+
self.sched_proc = subprocess.Popen(
|
133
|
+
self.avail_scheds[new_sched],
|
134
|
+
stderr=subprocess.DEVNULL,
|
135
|
+
stdout=subprocess.DEVNULL,
|
136
|
+
)
|
137
|
+
|
138
|
+
def close_sched(self):
|
139
|
+
if self.sched_proc is not None:
|
140
|
+
logger.info(f"Closing sched_ext scheduler '{self.old_sched}'.")
|
141
|
+
self.sched_proc.send_signal(signal.SIGINT)
|
142
|
+
self.sched_proc.wait()
|
143
|
+
self.sched_proc = None
|
144
|
+
|
145
|
+
def close(self):
|
146
|
+
self.close_sched()
|
@@ -0,0 +1,27 @@
|
|
1
|
+
title: Power
|
2
|
+
tags: [hide-title]
|
3
|
+
type: container
|
4
|
+
children:
|
5
|
+
profile:
|
6
|
+
type: multiple
|
7
|
+
# tags: [ordinal] # Steamdeck has issues with this
|
8
|
+
title: Power Profile
|
9
|
+
hint: >-
|
10
|
+
Allows setting the power profile of the system using Power Profiles Daemon.
|
11
|
+
options:
|
12
|
+
power-saver: Powersave
|
13
|
+
balanced: Balanced
|
14
|
+
performance: Performance
|
15
|
+
default: balanced
|
16
|
+
sched:
|
17
|
+
type: multiple
|
18
|
+
title: Custom Scheduler
|
19
|
+
hint: >-
|
20
|
+
Allows attaching a scheduler to the kernel sched_ext.
|
21
|
+
Schedulers need to be installed and kernel needs to support sched_ext.
|
22
|
+
options:
|
23
|
+
disabled: Disabled
|
24
|
+
scx_lavd: LAVD
|
25
|
+
scx_bpfland: bpfland
|
26
|
+
scx_rusty: rusty
|
27
|
+
default: disabled
|
@@ -165,6 +165,7 @@ class LenovoDriverPlugin(HHDPlugin):
|
|
165
165
|
conf["tdp.lenovo.tdp.mode"] = mode
|
166
166
|
elif new_mode:
|
167
167
|
mode = new_mode
|
168
|
+
conf["tdp.lenovo.tdp.mode"] = mode
|
168
169
|
else:
|
169
170
|
mode = conf["tdp.lenovo.tdp.mode"].to(str)
|
170
171
|
if mode is not None and mode != self.old_conf["tdp.mode"].to(str):
|
@@ -10,16 +10,20 @@ children:
|
|
10
10
|
modes:
|
11
11
|
quiet:
|
12
12
|
type: container
|
13
|
-
title: Quiet
|
13
|
+
title: Quiet
|
14
|
+
unit: 8W
|
14
15
|
balanced:
|
15
16
|
type: container
|
16
|
-
title: Balanced
|
17
|
+
title: Balanced
|
18
|
+
unit: 15W
|
17
19
|
performance:
|
18
20
|
type: container
|
19
|
-
title: Performance
|
21
|
+
title: Performance
|
22
|
+
unit: 20W
|
20
23
|
custom:
|
21
24
|
type: container
|
22
|
-
title: Custom
|
25
|
+
title: Custom
|
26
|
+
unit: → 25/30W
|
23
27
|
children:
|
24
28
|
tdp:
|
25
29
|
type: int
|
@@ -46,6 +50,12 @@ children:
|
|
46
50
|
hint: >-
|
47
51
|
Allows the device to boost by setting appropriate slow and fast TDPs.
|
48
52
|
|
53
|
+
cycle_info:
|
54
|
+
type: display
|
55
|
+
title: " "
|
56
|
+
tags: [non-essential]
|
57
|
+
default: "Legion L + Y changes TDP Mode"
|
58
|
+
|
49
59
|
ffss:
|
50
60
|
type: bool
|
51
61
|
title: Set Fan to Full Speed
|
@@ -256,7 +256,7 @@ def autodetect(existing: Sequence[HHDPlugin]) -> Sequence[HHDPlugin]:
|
|
256
256
|
default_tdp = 15
|
257
257
|
max_tdp = 30
|
258
258
|
|
259
|
-
if prod == "83E1":
|
259
|
+
if prod == "83E1" and not bool(os.environ.get("HHD_ADJ_ALLY")):
|
260
260
|
drivers.append(LenovoDriverPlugin())
|
261
261
|
drivers_matched = True
|
262
262
|
use_acpi_call = True
|
@@ -265,8 +265,9 @@ def autodetect(existing: Sequence[HHDPlugin]) -> Sequence[HHDPlugin]:
|
|
265
265
|
"ROG Ally RC71L" in prod
|
266
266
|
or "ROG Ally X RC72L" in prod
|
267
267
|
or bool(os.environ.get("HHD_ADJ_DEBUG"))
|
268
|
+
or bool(os.environ.get("HHD_ADJ_ALLY"))
|
268
269
|
):
|
269
|
-
drivers.append(AsusDriverPlugin())
|
270
|
+
drivers.append(AsusDriverPlugin("RC72L" in prod))
|
270
271
|
drivers_matched = True
|
271
272
|
|
272
273
|
if os.environ.get("HHD_ADJ_DEBUG") or os.environ.get("HHD_ENABLE_SMU"):
|
@@ -322,8 +323,10 @@ def autodetect(existing: Sequence[HHDPlugin]) -> Sequence[HHDPlugin]:
|
|
322
323
|
break
|
323
324
|
|
324
325
|
if not drivers:
|
325
|
-
|
326
|
-
|
326
|
+
from .drivers.general import GeneralPowerPlugin
|
327
|
+
|
328
|
+
logger.info(f"No tdp drivers found for this device, using generic plugin.")
|
329
|
+
return [GeneralPowerPlugin()]
|
327
330
|
|
328
331
|
return [
|
329
332
|
*drivers,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: adjustor
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.4.0
|
4
4
|
Summary: Adjustor, a userspace program for managing the TDP of handheld devices.
|
5
5
|
Author-email: Kapenekakis Antheas <pypi@antheas.dev>
|
6
6
|
Project-URL: Homepage, https://github.com/hhd-dev/adjustor
|
@@ -38,7 +38,7 @@ except intel handhelds and older prior to 6XXX AMD handhelds.
|
|
38
38
|
## TDP Control
|
39
39
|
For the ROG Ally, Ally X and Legion Go that have an ACPI/EC implementation for
|
40
40
|
bios and fan curves,
|
41
|
-
Adjustor uses the
|
41
|
+
Adjustor uses the manufacturer functions for setting TDP.
|
42
42
|
For the Allys, the asus-wmi kernel driver is used to set the tdp and manage the
|
43
43
|
fan curves.
|
44
44
|
For the Go, Lenovo's WMI methods are called through `acpi_call`, which will hopefully
|
@@ -53,14 +53,14 @@ For more, see [AMD TDP Control Details](#amd-tdp).
|
|
53
53
|
In all cases, there are checks to ensure that the TDP is within the safe range
|
54
54
|
of the processors.
|
55
55
|
|
56
|
-
## Energy Management
|
57
|
-
Adjustor can also manage the energy profile of the processor, by setting EPP
|
56
|
+
## Energy Management in Handhelds
|
57
|
+
Adjustor can also manage the energy profile of the processor in handhelds, by setting EPP
|
58
58
|
and proper frequency values.
|
59
59
|
After we transitioned people away from Decky plugins (which had some governor controls)
|
60
60
|
to using Handheld Daemon for TDP, we found that Power Profiles Daemon (PPD)
|
61
61
|
would use aggressive CPU values.
|
62
62
|
These values are optimized for devices that have a dedicated power budget for the CPU
|
63
|
-
(e.g., laptops, desktops), which
|
63
|
+
(e.g., laptops, desktops), which causes undesirable behavior handhelds.
|
64
64
|
|
65
65
|
For example, the balanced PPD profile would set EPP to balance_performance and
|
66
66
|
enable CPU boost, which would increase the draw of the CPU during gaming by 2W
|
@@ -91,6 +91,48 @@ TDP range instead of CPU values (which is the user's expectation).
|
|
91
91
|
Of course, depending on TDP and user preference, the CPU governor values will be set
|
92
92
|
accordingly.
|
93
93
|
|
94
|
+
## Energy Management in other computers
|
95
|
+
As we design Handheld Daemon to be enabled in more Deck style devices (e.g., HTPCs),
|
96
|
+
these devices have different power requirements and processors (e.g., Intel), which
|
97
|
+
are better managed with Power Profiles Daemon.
|
98
|
+
It is the aim of the Handheld Daemon project to become a general Deck style session
|
99
|
+
manager for anything gamescope related, with useful features for all devices.
|
100
|
+
|
101
|
+
In these cases, starting with 3.4, for devices that are not in the CPU/device
|
102
|
+
whitelist (includes only AMD U series APUs and handhelds), Adjustor contains a
|
103
|
+
general energy management plugin that allows for switching the PPD power profile
|
104
|
+
from game mode.
|
105
|
+
In addition, it supports [sched_ext schedulers](#sched_ext).
|
106
|
+
|
107
|
+
This means that for general devices, Handheld Daemon uses PPD, and for handhelds,
|
108
|
+
Handheld Daemon becomes PPD.
|
109
|
+
This can be confusing for distribution maintainers and users, as they can and
|
110
|
+
should install both (e.g., Adjustor uses the Power Profile Daemon polkits).
|
111
|
+
|
112
|
+
In any case, Adjustor will never break/conflict with PPD and contains helpful
|
113
|
+
messages about disabling PPD in case the optimized handheld plugin is loaded.
|
114
|
+
For distribution maintainers that ship both and want Handheld Daemon to work
|
115
|
+
without user intervention, the environment variable `HHD_PPD_MASK` is provided.
|
116
|
+
If and only if it is set e.g., by using a systemd service extension, Handheld Daemon
|
117
|
+
will mask and disable PPD if optimized energy management is supported and enabled (e.g., for handhelds).
|
118
|
+
If the general plugin is loaded, it will unmask PPD during startup.
|
119
|
+
This means that Power Management will work properly for all devices without manual
|
120
|
+
intervention or whitelisting by distribution maintainers.
|
121
|
+
|
122
|
+
## Sched_ext<a name="sched-ext"></a>
|
123
|
+
Starting with version 3.3, Adjustor can attach sched_ext schedulers to the
|
124
|
+
kernel if those are supported and installed.
|
125
|
+
Adjustor manages the lifetime of the scheduler, including launching and attaching
|
126
|
+
it, without using a systemd service, and is fully responsive to quirk scheduler
|
127
|
+
switches.
|
128
|
+
|
129
|
+
Schedulers are whitelisted in a case by case basis, with LAVD, rusty, and bpfland
|
130
|
+
being supported in the current version (the `scx_` namespace is crowded with e.g.,
|
131
|
+
test schedulers).
|
132
|
+
Of course, only installed schedulers are shown if and only if the kernel supports
|
133
|
+
them.
|
134
|
+
Get in touch to add your favorite scheduler, as it is a single line change.
|
135
|
+
|
94
136
|
## AMD TDP Control Details<a name="amd-tdp"></a>
|
95
137
|
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
96
138
|
of AMD, which exposes a superset of the parameters that can be currently found in
|
@@ -99,7 +141,7 @@ This vendor interface is part of the ACPI ASL library, and provided through the
|
|
99
141
|
ALIB method 0x0C.
|
100
142
|
The underlying implementation of the interface is SMU calls.
|
101
143
|
This means that as long as the kernel module `acpi_call` is loaded, Adjustor
|
102
|
-
can control TDP in
|
144
|
+
can control TDP in a similar way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
|
103
145
|
|
104
146
|
The ABI of this vendor function (as it is provided to manufacturers) can be
|
105
147
|
considered mostly stable, so little work is needed between subsequent
|
@@ -109,8 +151,9 @@ Of course, support for processors is only added after the ACPI bindings have
|
|
109
151
|
been reviewed, to avoid surprises.
|
110
152
|
Both the Ally and Legion Go use this function, in the exact same way, so setting
|
111
153
|
TDP with it is very stable, and we have had no reported crashes.
|
112
|
-
It
|
113
|
-
manufacturer functions
|
154
|
+
It can not be used and is not used with those devices, however, as the
|
155
|
+
manufacturer functions would interfere and provide a better user experience,
|
156
|
+
such as setting appropriate fan curves and changing the power light color on the Legion Go.
|
114
157
|
|
115
158
|
Unfortunately for devices that do have an ACPI/EC implementation for TDP, there
|
116
159
|
is no official way of setting TDP on demand, either on Linux or Windows, with
|
@@ -27,6 +27,8 @@ src/adjustor/drivers/amd/ppd.py
|
|
27
27
|
src/adjustor/drivers/amd/settings.yml
|
28
28
|
src/adjustor/drivers/asus/__init__.py
|
29
29
|
src/adjustor/drivers/asus/settings.yml
|
30
|
+
src/adjustor/drivers/general/__init__.py
|
31
|
+
src/adjustor/drivers/general/settings.yml
|
30
32
|
src/adjustor/drivers/lenovo/__init__.py
|
31
33
|
src/adjustor/drivers/lenovo/settings.yml
|
32
34
|
src/adjustor/drivers/smu/__init__.py
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{adjustor-3.3.1 → adjustor-3.4.0}/src/adjustor/drivers/amd/power-profiles-daemon.dbus.xml.in
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{adjustor-3.3.1 → adjustor-3.4.0}/usr/share/dbus-1/system.d/hhd-net.hadess.PowerProfiles.conf
RENAMED
File without changes
|