adjustor 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- adjustor-0.2.0/MANIFEST.in +2 -0
- {adjustor-0.1.0/src/adjustor.egg-info → adjustor-0.2.0}/PKG-INFO +20 -7
- {adjustor-0.1.0 → adjustor-0.2.0}/pyproject.toml +1 -1
- {adjustor-0.1.0 → adjustor-0.2.0}/readme.md +19 -6
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/core/const.py +13 -1
- adjustor-0.2.0/src/adjustor/drivers/lenovo/settings.yml +152 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/drivers/smu/__init__.py +58 -30
- adjustor-0.2.0/src/adjustor/drivers/smu/qam.yml +27 -0
- adjustor-0.2.0/src/adjustor/drivers/smu/smu.yml +155 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/hhd.py +31 -15
- adjustor-0.2.0/src/adjustor/settings.yml +21 -0
- {adjustor-0.1.0 → adjustor-0.2.0/src/adjustor.egg-info}/PKG-INFO +20 -7
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor.egg-info/SOURCES.txt +6 -1
- {adjustor-0.1.0 → adjustor-0.2.0}/LICENSE +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/setup.cfg +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/__init__.py +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/__main__.py +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/core/acpi.py +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/core/alib.py +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/core/asus.py +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/core/lenovo.py +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/drivers/__init__.py +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor/drivers/lenovo/__init__.py +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor.egg-info/dependency_links.txt +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor.egg-info/entry_points.txt +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor.egg-info/requires.txt +0 -0
- {adjustor-0.1.0 → adjustor-0.2.0}/src/adjustor.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: adjustor
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.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
|
@@ -35,22 +35,35 @@ As part of the latest Lenovo bios, it also allows for setting a custom fan curve
|
|
35
35
|
for the Legion Go.
|
36
36
|
|
37
37
|
## AMD TDP Control
|
38
|
-
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
39
|
-
which exposes a superset of the parameters that can be found in
|
38
|
+
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
39
|
+
of AMD, which exposes a superset of the parameters that can be currently found in
|
40
40
|
[RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/), through ACPI.
|
41
|
-
This vendor
|
41
|
+
This vendor interface is part of the ACPI ASL library, and provided through the
|
42
42
|
ALIB method 0x0C.
|
43
|
+
The underlying implementation of the interface is SMU calls.
|
43
44
|
This means that as long as the kernel module `acpi_call` is loaded, Adjustor
|
44
45
|
can control TDP in an equivalent way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
|
45
46
|
|
46
|
-
|
47
|
-
|
47
|
+
Right now, Adjustor only implements a subset of useful ALIB parameters that are
|
48
|
+
well documented.
|
49
|
+
In addition, ALIB does not provide a way for reading the performance metrics table,
|
50
|
+
so Adjustor can only write (not read) TDP values.
|
48
51
|
From reverse engineering the Legion Go (see [here](./alib.md)), and seeing how it
|
49
52
|
interacts with ALIB, it was found that there are at least 10 parameters which control
|
50
53
|
the method STTv2 and are not part of RyzenAdj or have been documented elsewhere.
|
51
54
|
|
52
55
|
## Installation
|
53
|
-
|
56
|
+
Adjustor is available on [AUR](https://aur.archlinux.org/packages/adjustor)
|
57
|
+
and provided Handheld Daemon has been installed through
|
58
|
+
[AUR](https://aur.archlinux.org/packages/hhd) too, it will load it automatically
|
59
|
+
on restart.
|
60
|
+
COPR coming soon.
|
61
|
+
|
62
|
+
Alternatively, on a local install of Handheld Daemon you may:
|
63
|
+
```bash
|
64
|
+
~/.local/share/hhd/venv/bin/pip install --upgrade adjustor
|
65
|
+
```
|
66
|
+
However, the autoupdater in Handheld Daemon does not support updating yet.
|
54
67
|
|
55
68
|
## Development
|
56
69
|
Install to the same virtual environment as hhd to have Adjustor picked up
|
@@ -18,22 +18,35 @@ As part of the latest Lenovo bios, it also allows for setting a custom fan curve
|
|
18
18
|
for the Legion Go.
|
19
19
|
|
20
20
|
## AMD TDP Control
|
21
|
-
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
22
|
-
which exposes a superset of the parameters that can be found in
|
21
|
+
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
22
|
+
of AMD, which exposes a superset of the parameters that can be currently found in
|
23
23
|
[RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/), through ACPI.
|
24
|
-
This vendor
|
24
|
+
This vendor interface is part of the ACPI ASL library, and provided through the
|
25
25
|
ALIB method 0x0C.
|
26
|
+
The underlying implementation of the interface is SMU calls.
|
26
27
|
This means that as long as the kernel module `acpi_call` is loaded, Adjustor
|
27
28
|
can control TDP in an equivalent way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
Right now, Adjustor only implements a subset of useful ALIB parameters that are
|
31
|
+
well documented.
|
32
|
+
In addition, ALIB does not provide a way for reading the performance metrics table,
|
33
|
+
so Adjustor can only write (not read) TDP values.
|
31
34
|
From reverse engineering the Legion Go (see [here](./alib.md)), and seeing how it
|
32
35
|
interacts with ALIB, it was found that there are at least 10 parameters which control
|
33
36
|
the method STTv2 and are not part of RyzenAdj or have been documented elsewhere.
|
34
37
|
|
35
38
|
## Installation
|
36
|
-
|
39
|
+
Adjustor is available on [AUR](https://aur.archlinux.org/packages/adjustor)
|
40
|
+
and provided Handheld Daemon has been installed through
|
41
|
+
[AUR](https://aur.archlinux.org/packages/hhd) too, it will load it automatically
|
42
|
+
on restart.
|
43
|
+
COPR coming soon.
|
44
|
+
|
45
|
+
Alternatively, on a local install of Handheld Daemon you may:
|
46
|
+
```bash
|
47
|
+
~/.local/share/hhd/venv/bin/pip install --upgrade adjustor
|
48
|
+
```
|
49
|
+
However, the autoupdater in Handheld Daemon does not support updating yet.
|
37
50
|
|
38
51
|
## Development
|
39
52
|
Install to the same virtual environment as hhd to have Adjustor picked up
|
@@ -1,5 +1,12 @@
|
|
1
1
|
from .alib import A, D, DeviceParams, AlibParams
|
2
2
|
|
3
|
+
ROG_ALLY_PP_MAP = [
|
4
|
+
("low-power", 0),
|
5
|
+
("quiet", 0),
|
6
|
+
("balanced", 13),
|
7
|
+
("performance", 20),
|
8
|
+
]
|
9
|
+
|
3
10
|
ALIB_PARAMS = {
|
4
11
|
# TDPs
|
5
12
|
"stapm_limit": A(0x05, 0, 54, 1000),
|
@@ -32,9 +39,14 @@ DEV_PARAMS_6040: dict[str, DeviceParams] = DEV_PARAMS_7040
|
|
32
39
|
|
33
40
|
DEV_PARAMS_LEGO = DEV_PARAMS_7040
|
34
41
|
|
42
|
+
DEV_DATA: dict[str, tuple[dict[str, DeviceParams], dict[str, AlibParams], bool]] = {
|
43
|
+
"NEO-01": (DEV_PARAMS_7040, ALIB_PARAMS_7040, False),
|
44
|
+
"83E1": (DEV_PARAMS_LEGO, ALIB_PARAMS_7040, False),
|
45
|
+
}
|
46
|
+
|
35
47
|
CPU_DATA: dict[str, tuple[dict[str, DeviceParams], dict[str, AlibParams]]] = {
|
36
48
|
"AMD Ryzen Z1 Extreme": (DEV_PARAMS_7040, ALIB_PARAMS_7040),
|
37
|
-
"AMD Ryzen 7
|
49
|
+
"AMD Ryzen 7 7840U": (DEV_PARAMS_7040, ALIB_PARAMS_7040),
|
38
50
|
# GPD Win 4
|
39
51
|
# model name : AMD Ryzen 7 6800U with Radeon Graphics
|
40
52
|
# 28W works fine, 30W is pushing it
|
@@ -0,0 +1,152 @@
|
|
1
|
+
title: Lenovo TDP
|
2
|
+
type: container
|
3
|
+
hint: >-
|
4
|
+
Uses the interface of Legion Space to set the TDP of the device.
|
5
|
+
children:
|
6
|
+
tdp:
|
7
|
+
type: mode
|
8
|
+
title: TDP Mode
|
9
|
+
modes:
|
10
|
+
quiet:
|
11
|
+
type: container
|
12
|
+
title: Quiet (8W)
|
13
|
+
balanced:
|
14
|
+
type: container
|
15
|
+
title: Balanced (15W)
|
16
|
+
performance:
|
17
|
+
type: container
|
18
|
+
title: Performance (20W)
|
19
|
+
custom:
|
20
|
+
type: container
|
21
|
+
title: Custom (up to 25-30W)
|
22
|
+
children:
|
23
|
+
tdp:
|
24
|
+
type: int
|
25
|
+
title: TDP
|
26
|
+
hint: >-
|
27
|
+
Maximum TDP in steady state.
|
28
|
+
Sets STAMP, Skin Power Limit to it (steady state).
|
29
|
+
If boost is enabled, interpolates slow and fast TDPs based
|
30
|
+
on those provided by Lenovo.
|
31
|
+
If disabled, it sets the Slow limit equal to TDP and the Fast
|
32
|
+
limit to +2W.
|
33
|
+
Boost is recommended for desktop use.
|
34
|
+
|
35
|
+
tags: [advanced]
|
36
|
+
min: 0
|
37
|
+
max: 30
|
38
|
+
step: 1
|
39
|
+
unit: W
|
40
|
+
boost:
|
41
|
+
type: bool
|
42
|
+
title: Boost
|
43
|
+
default: True
|
44
|
+
hint: >-
|
45
|
+
Allows the device to boost by setting appropriate slow and fast TDPs.
|
46
|
+
|
47
|
+
ffss:
|
48
|
+
type: bool
|
49
|
+
title: Set Fan to Full Speed
|
50
|
+
|
51
|
+
fan:
|
52
|
+
type: mode
|
53
|
+
title: Custom Fan Curve
|
54
|
+
hint: >-
|
55
|
+
Allows you to set a custom fan curve. This fan curve is only officially
|
56
|
+
supported on custom mode, but you can nevertheless use it in other power
|
57
|
+
modes at your own risk.
|
58
|
+
|
59
|
+
This fan curve needs to be reapplied every time you switch TDP modes.
|
60
|
+
default: disabled
|
61
|
+
modes:
|
62
|
+
disabled:
|
63
|
+
type: container
|
64
|
+
title: Disabled
|
65
|
+
hint: >-
|
66
|
+
Lets Legion GO manage the curve on its own. Setting this option will cause
|
67
|
+
a mode change to reset the fan curve.
|
68
|
+
manual:
|
69
|
+
type: container
|
70
|
+
title: Manual
|
71
|
+
children:
|
72
|
+
apply:
|
73
|
+
title: Apply Fan Curve
|
74
|
+
type: action
|
75
|
+
hint: >-
|
76
|
+
Reapplies the fan curve below if it got reset.
|
77
|
+
|
78
|
+
status:
|
79
|
+
title: Fan Curve Status
|
80
|
+
type: display
|
81
|
+
# For translation
|
82
|
+
choices:
|
83
|
+
startup: Unknown
|
84
|
+
not-set: Not Set
|
85
|
+
set: Set
|
86
|
+
|
87
|
+
|
88
|
+
st10: &speed_template
|
89
|
+
title: 10C
|
90
|
+
hint: Sets the speed at the named temperature.
|
91
|
+
tags: [slim]
|
92
|
+
type: int
|
93
|
+
min: 0
|
94
|
+
smin: 44
|
95
|
+
max: 115
|
96
|
+
step: 2
|
97
|
+
unit: "%"
|
98
|
+
|
99
|
+
st20:
|
100
|
+
<<: *speed_template
|
101
|
+
title: 20C
|
102
|
+
smin: 48
|
103
|
+
st30:
|
104
|
+
<<: *speed_template
|
105
|
+
title: 30C
|
106
|
+
smin: 55
|
107
|
+
st40:
|
108
|
+
<<: *speed_template
|
109
|
+
title: 40C
|
110
|
+
smin: 60
|
111
|
+
st50:
|
112
|
+
<<: *speed_template
|
113
|
+
title: 50C
|
114
|
+
smin: 71
|
115
|
+
st60:
|
116
|
+
<<: *speed_template
|
117
|
+
title: 60C
|
118
|
+
smin: 79
|
119
|
+
st70:
|
120
|
+
<<: *speed_template
|
121
|
+
title: 70C
|
122
|
+
smin: 87
|
123
|
+
st80:
|
124
|
+
<<: *speed_template
|
125
|
+
title: 80C
|
126
|
+
smin: 87
|
127
|
+
st90:
|
128
|
+
<<: *speed_template
|
129
|
+
title: 90C
|
130
|
+
smin: 100
|
131
|
+
st100:
|
132
|
+
<<: *speed_template
|
133
|
+
title: 100C
|
134
|
+
smin: 100
|
135
|
+
|
136
|
+
enforce_limits:
|
137
|
+
title: Enforce Windows Minimums
|
138
|
+
type: bool
|
139
|
+
default: True
|
140
|
+
hint: >-
|
141
|
+
Enforce the minimum fan curve from Legion Space.
|
142
|
+
|
143
|
+
reset:
|
144
|
+
title: Restore Default
|
145
|
+
type: action
|
146
|
+
hint: >-
|
147
|
+
Resets to the original fan curve provided by Lenovo in BIOS V28.
|
148
|
+
|
149
|
+
power_light:
|
150
|
+
tags: [advanced]
|
151
|
+
type: bool
|
152
|
+
title: Enable Power Light
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
import time
|
2
3
|
|
3
4
|
from hhd.plugins import Context, HHDPlugin, load_relative_yaml
|
4
5
|
from hhd.plugins.conf import Config
|
@@ -38,10 +39,15 @@ def get_platform_profile():
|
|
38
39
|
return None
|
39
40
|
|
40
41
|
|
42
|
+
PP_DELAY = 0.2
|
43
|
+
|
44
|
+
|
41
45
|
class SmuQamPlugin(HHDPlugin):
|
42
46
|
|
43
47
|
def __init__(
|
44
|
-
self,
|
48
|
+
self,
|
49
|
+
dev: dict[str, DeviceParams],
|
50
|
+
pp_map: list[tuple[str, int]] | None,
|
45
51
|
) -> None:
|
46
52
|
self.name = f"adjustor_smu_qam"
|
47
53
|
self.priority = 7
|
@@ -53,14 +59,16 @@ class SmuQamPlugin(HHDPlugin):
|
|
53
59
|
self.emit = None
|
54
60
|
self.old_conf = None
|
55
61
|
|
56
|
-
self.check_pp = platform_profile
|
57
|
-
self.old_pp = None
|
58
|
-
self.has_pp = False
|
59
|
-
|
60
62
|
self.old_tdp = None
|
61
63
|
self.old_boost = None
|
62
64
|
self.is_set = False
|
63
65
|
|
66
|
+
self.pp_map = pp_map
|
67
|
+
if pp_map:
|
68
|
+
self.pps = get_platform_choices() or []
|
69
|
+
else:
|
70
|
+
self.pps = []
|
71
|
+
|
64
72
|
def settings(self):
|
65
73
|
if not self.enabled:
|
66
74
|
self.initialized = False
|
@@ -69,18 +77,6 @@ class SmuQamPlugin(HHDPlugin):
|
|
69
77
|
self.initialized = True
|
70
78
|
out = {"tdp": {"qam": load_relative_yaml("qam.yml")}}
|
71
79
|
|
72
|
-
# Limit platform profile choices or remove
|
73
|
-
choices = get_platform_choices()
|
74
|
-
if choices and self.check_pp:
|
75
|
-
options = out["tdp"]["qam"]["children"]["platform_profile"]["options"]
|
76
|
-
for c in list(options):
|
77
|
-
if c not in choices:
|
78
|
-
del options[c]
|
79
|
-
self.has_pp = True
|
80
|
-
else:
|
81
|
-
del out["tdp"]["qam"]["children"]["platform_profile"]
|
82
|
-
self.has_pp = False
|
83
|
-
|
84
80
|
# Set device limits based on stapm
|
85
81
|
lims = self.dev.get("skin_limit", self.dev.get("stapm_limit", None))
|
86
82
|
assert (
|
@@ -112,17 +108,6 @@ class SmuQamPlugin(HHDPlugin):
|
|
112
108
|
if not self.enabled or not self.initialized:
|
113
109
|
return
|
114
110
|
|
115
|
-
if self.has_pp:
|
116
|
-
cpp = conf["tdp.qam.platform_profile"].to(str)
|
117
|
-
|
118
|
-
if cpp and cpp != self.old_pp:
|
119
|
-
logger.info(f"Setting platform profile to '{cpp}'")
|
120
|
-
set_platform_profile(cpp)
|
121
|
-
|
122
|
-
pp = get_platform_profile()
|
123
|
-
conf["tdp.qam.platform_profile"] = pp
|
124
|
-
self.old_pp = pp
|
125
|
-
|
126
111
|
new_tdp = conf["tdp.qam.tdp"].to(int)
|
127
112
|
new_boost = conf["tdp.qam.boost"].to(bool)
|
128
113
|
changed = (
|
@@ -135,6 +120,14 @@ class SmuQamPlugin(HHDPlugin):
|
|
135
120
|
|
136
121
|
conf["tdp.smu.std.skin_limit"] = new_tdp
|
137
122
|
conf["tdp.smu.std.stapm_limit"] = new_tdp
|
123
|
+
|
124
|
+
if self.pp_map and conf["tdp.smu.platform_profile"].to(str) != "disabled":
|
125
|
+
pp = self.pp_map[0][0]
|
126
|
+
for npp, tdp in self.pp_map:
|
127
|
+
if tdp < new_tdp and npp in self.pps:
|
128
|
+
pp = npp
|
129
|
+
conf["tdp.smu.platform_profile"] = pp
|
130
|
+
|
138
131
|
if new_boost:
|
139
132
|
try:
|
140
133
|
fmax = self.dev["fast_limit"].smax
|
@@ -174,6 +167,7 @@ class SmuDriverPlugin(HHDPlugin):
|
|
174
167
|
self,
|
175
168
|
dev: dict[str, DeviceParams],
|
176
169
|
cpu: dict[str, AlibParams],
|
170
|
+
platform_profile: bool = True,
|
177
171
|
) -> None:
|
178
172
|
self.name = f"adjustor_smu"
|
179
173
|
self.priority = 9
|
@@ -185,7 +179,11 @@ class SmuDriverPlugin(HHDPlugin):
|
|
185
179
|
self.dev = dev
|
186
180
|
self.cpu = cpu
|
187
181
|
|
182
|
+
self.check_pp = platform_profile
|
183
|
+
self.has_pp = False
|
184
|
+
self.old_pp = None
|
188
185
|
self.old_vals = {}
|
186
|
+
self.is_set = False
|
189
187
|
|
190
188
|
for k in dev:
|
191
189
|
assert (
|
@@ -203,6 +201,18 @@ class SmuDriverPlugin(HHDPlugin):
|
|
203
201
|
}
|
204
202
|
}
|
205
203
|
|
204
|
+
# Limit platform profile choices or remove
|
205
|
+
choices = get_platform_choices()
|
206
|
+
if choices and self.check_pp:
|
207
|
+
options = out["tdp"]["smu"]["children"]["platform_profile"]["options"]
|
208
|
+
for c in list(options):
|
209
|
+
if c not in choices and c != "disabled":
|
210
|
+
del options[c]
|
211
|
+
self.has_pp = True
|
212
|
+
else:
|
213
|
+
del out["tdp"]["smu"]["children"]["platform_profile"]
|
214
|
+
self.has_pp = False
|
215
|
+
|
206
216
|
# Remove unsupported instructions
|
207
217
|
# Add absolute limits based on CPU
|
208
218
|
std = out["tdp"]["smu"]["children"]["std"]["children"]
|
@@ -270,19 +280,37 @@ class SmuDriverPlugin(HHDPlugin):
|
|
270
280
|
new_vals[k] = v
|
271
281
|
|
272
282
|
if set(new_vals.items()) != set(self.old_vals.items()):
|
273
|
-
|
283
|
+
self.is_set = False
|
284
|
+
|
285
|
+
if self.has_pp:
|
286
|
+
new_pp = conf["tdp.smu.platform_profile"].to(str)
|
287
|
+
if new_pp != self.old_pp and new_pp != "disabled":
|
288
|
+
self.is_set = False
|
289
|
+
self.old_pp = new_pp
|
274
290
|
|
275
291
|
if conf["tdp.smu.apply"].to(bool):
|
276
292
|
conf["tdp.smu.apply"] = False
|
293
|
+
|
294
|
+
if self.has_pp:
|
295
|
+
cpp = conf["tdp.smu.platform_profile"].to(str)
|
296
|
+
if cpp != "disabled":
|
297
|
+
logger.info(f"Setting platform profile to '{cpp}'")
|
298
|
+
set_platform_profile(cpp)
|
299
|
+
time.sleep(PP_DELAY)
|
300
|
+
|
277
301
|
alib(
|
278
302
|
new_vals,
|
279
303
|
self.cpu,
|
280
304
|
limit="device" if self.enforce_limits else "cpu",
|
281
305
|
dev=self.dev,
|
282
306
|
)
|
283
|
-
|
307
|
+
self.is_set = True
|
284
308
|
|
285
309
|
self.old_vals = new_vals
|
310
|
+
if self.is_set:
|
311
|
+
conf["tdp.smu.status"] = "Set"
|
312
|
+
else:
|
313
|
+
conf["tdp.smu.status"] = "Not Set"
|
286
314
|
|
287
315
|
def close(self):
|
288
316
|
pass
|
@@ -0,0 +1,27 @@
|
|
1
|
+
title: Quick Settings
|
2
|
+
type: container
|
3
|
+
children:
|
4
|
+
tdp:
|
5
|
+
title: TDP
|
6
|
+
hint: >-
|
7
|
+
Controls all Ryzen SMU settings through preset curves.
|
8
|
+
type: int
|
9
|
+
step: 1
|
10
|
+
unit: W
|
11
|
+
|
12
|
+
boost:
|
13
|
+
title: Allow Boost
|
14
|
+
type: bool
|
15
|
+
default: True
|
16
|
+
|
17
|
+
apply:
|
18
|
+
title: Apply TDP
|
19
|
+
type: action
|
20
|
+
|
21
|
+
status:
|
22
|
+
title: TDP Status
|
23
|
+
type: display
|
24
|
+
# For translation
|
25
|
+
choices:
|
26
|
+
uninit: Not Set
|
27
|
+
init: Set
|
@@ -0,0 +1,155 @@
|
|
1
|
+
type: container
|
2
|
+
title: Advanced Configurator
|
3
|
+
tags: [advanced, expert]
|
4
|
+
hint: >-
|
5
|
+
children:
|
6
|
+
apply:
|
7
|
+
type: action
|
8
|
+
title: Apply Settings
|
9
|
+
|
10
|
+
status:
|
11
|
+
title: TDP Status
|
12
|
+
type: display
|
13
|
+
# For translation
|
14
|
+
choices:
|
15
|
+
uninit: Not Set
|
16
|
+
init: Set
|
17
|
+
|
18
|
+
platform_profile:
|
19
|
+
type: multiple
|
20
|
+
title: Platform Profile
|
21
|
+
default: balanced
|
22
|
+
options:
|
23
|
+
disabled: Not Set
|
24
|
+
low-power: Low Power
|
25
|
+
cool: Cool
|
26
|
+
quiet: Quiet
|
27
|
+
balanced: Balanced
|
28
|
+
balanced-performance: Balanced Performance
|
29
|
+
performance: Performance
|
30
|
+
|
31
|
+
std:
|
32
|
+
tags: [advanced]
|
33
|
+
type: container
|
34
|
+
title: Standard Parameters
|
35
|
+
hint: >-
|
36
|
+
Standard TDP parameters for Ryzen processors. All need to be set to
|
37
|
+
properly control the TDP of the device.
|
38
|
+
|
39
|
+
Ryzen processors have 2 modes: STTv2 and STAPM (legacy). AMD suggests to
|
40
|
+
manufacturers to use STTv2, which makes the Legion Go the only device to
|
41
|
+
offer the STAPM alternative through a BIOS setting.
|
42
|
+
|
43
|
+
In STTv2, the device will keep boosting until the "skin" of the device
|
44
|
+
(hottest user accessible spot) reaches a manufacturer set temperature.
|
45
|
+
Then, the device will use the Skin Temp TDP limit.
|
46
|
+
In STAPM, the device averages the TDP values from the 1-3 previous minutes
|
47
|
+
and keeps that value under the STAPM TDP limit.
|
48
|
+
Either mode ignores the other mode's limit (STAPM limit does nothing on
|
49
|
+
STT and Skin Temp Limit does nothing on STAPM), so both should be set.
|
50
|
+
|
51
|
+
The Fast and Slow limits control boosting behavior.
|
52
|
+
The Fast TDP limit is the actual max TDP value of the device.
|
53
|
+
Then,the Slow TDP limit averages the last 10-20s of TDP values and keeps
|
54
|
+
the value below it.
|
55
|
+
|
56
|
+
# The Energy Policy is set automatically by the processor when connecting
|
57
|
+
# to a charger.
|
58
|
+
# It affects power consumption by around 2W, and its effect is mostly to
|
59
|
+
# allow or block the processor from downclocking below 1.6GHz.
|
60
|
+
# The battery profile was found to have an effect on VRR for the Ally.
|
61
|
+
|
62
|
+
children:
|
63
|
+
fast_limit:
|
64
|
+
type: int
|
65
|
+
title: Fast TDP Limit
|
66
|
+
min: 0
|
67
|
+
max: 55
|
68
|
+
step: 1
|
69
|
+
unit: W
|
70
|
+
default: 32
|
71
|
+
|
72
|
+
slow_limit:
|
73
|
+
type: int
|
74
|
+
title: Slow TDP Limit
|
75
|
+
min: 0
|
76
|
+
max: 55
|
77
|
+
step: 1
|
78
|
+
unit: W
|
79
|
+
default: 25
|
80
|
+
|
81
|
+
skin_limit:
|
82
|
+
type: int
|
83
|
+
title: Skin Temp TDP Limit
|
84
|
+
min: 0
|
85
|
+
max: 55
|
86
|
+
step: 1
|
87
|
+
unit: W
|
88
|
+
default: 15
|
89
|
+
|
90
|
+
stapm_limit:
|
91
|
+
type: int
|
92
|
+
title: STAPM TDP Limit
|
93
|
+
min: 0
|
94
|
+
max: 55
|
95
|
+
step: 1
|
96
|
+
unit: W
|
97
|
+
default: 15
|
98
|
+
|
99
|
+
# acdc:
|
100
|
+
# type: multiple
|
101
|
+
# title: Energy Policy Override
|
102
|
+
# options:
|
103
|
+
# disabled: Disabled
|
104
|
+
# power_saving: Battery
|
105
|
+
# max_performance: Plugged In
|
106
|
+
|
107
|
+
adv:
|
108
|
+
tags: [advanced]
|
109
|
+
type: container
|
110
|
+
title: Advanced Parameters
|
111
|
+
hint: >-
|
112
|
+
The Advanced Parameters below control boosting behavior and need to be
|
113
|
+
adjusted per device depending on its cooling system.
|
114
|
+
They mostly affect boosting behavior, which is important for desktop use.
|
115
|
+
|
116
|
+
The exception is the Temp Target (TCTL), which controls the max temperature
|
117
|
+
of the CPU die. On most devices, it can safely be raised up to 100C.
|
118
|
+
However, if a temperature spike makes the chip reach 105C, it will enter
|
119
|
+
a thermal protection mode, which is 5W, for a couple of minutes.
|
120
|
+
|
121
|
+
The integration times for Slow TDP and STAPM influence how many previous
|
122
|
+
TDP values the CPU will average to calculate its current Slow and STAPM
|
123
|
+
TDP values.
|
124
|
+
|
125
|
+
children:
|
126
|
+
temp_target:
|
127
|
+
type: int
|
128
|
+
title: Temp Target (TCTL)
|
129
|
+
min: 0
|
130
|
+
max: 105
|
131
|
+
default: 95
|
132
|
+
unit: C
|
133
|
+
|
134
|
+
slow_time:
|
135
|
+
type: int
|
136
|
+
title: Slow Limit Integration Time
|
137
|
+
min: 0
|
138
|
+
max: 20
|
139
|
+
step: 5
|
140
|
+
unit: s
|
141
|
+
default: 10
|
142
|
+
|
143
|
+
stapm_time:
|
144
|
+
type: int
|
145
|
+
title: STAPM Limit Integration Time
|
146
|
+
min: 0
|
147
|
+
max: 300
|
148
|
+
step: 5
|
149
|
+
unit: s
|
150
|
+
default: 200
|
151
|
+
|
152
|
+
enable:
|
153
|
+
type: bool
|
154
|
+
title: Enable Advanced Parameters
|
155
|
+
default: False
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from adjustor.core.acpi import initialize, check_perms
|
2
2
|
|
3
3
|
from typing import Sequence
|
4
|
-
from adjustor.core.const import CPU_DATA
|
4
|
+
from adjustor.core.const import CPU_DATA, ROG_ALLY_PP_MAP, DEV_DATA
|
5
5
|
|
6
6
|
import os
|
7
7
|
from hhd.plugins import (
|
@@ -56,7 +56,10 @@ class AdjustorPlugin(HHDPlugin):
|
|
56
56
|
self.enfoce_limits = True
|
57
57
|
|
58
58
|
def settings(self) -> HHDSettings:
|
59
|
-
|
59
|
+
out = {"tdp": {"general": load_relative_yaml("settings.yml")}}
|
60
|
+
if os.environ.get("HHD_ADJ_ENABLE_TDP"):
|
61
|
+
out['tdp']['general']['children']['enable']['default'] = True
|
62
|
+
return out
|
60
63
|
|
61
64
|
def open(
|
62
65
|
self,
|
@@ -91,27 +94,40 @@ def autodetect(existing: Sequence[HHDPlugin]) -> Sequence[HHDPlugin]:
|
|
91
94
|
cpuinfo = f.read().strip()
|
92
95
|
|
93
96
|
drivers_matched = False
|
94
|
-
legion_go = False
|
95
97
|
if prod == "83E1":
|
96
98
|
drivers.append(LenovoDriverPlugin())
|
97
99
|
drivers_matched = True
|
98
|
-
legion_go = True
|
99
100
|
|
100
|
-
if (
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
101
|
+
if os.environ.get("HHD_ADJ_DEBUG") or os.environ.get("HHD_ENABLE_SMU"):
|
102
|
+
drivers_matched = False
|
103
|
+
|
104
|
+
if not drivers_matched and prod in DEV_DATA:
|
105
|
+
dev, cpu, pp_enable = DEV_DATA[prod]
|
106
|
+
pp_enable |= bool(os.environ.get("HHD_ADJ_DEBUG"))
|
107
|
+
drivers.append(
|
108
|
+
SmuDriverPlugin(
|
109
|
+
dev,
|
110
|
+
cpu,
|
111
|
+
platform_profile=pp_enable,
|
112
|
+
)
|
113
|
+
)
|
114
|
+
drivers.append(
|
115
|
+
SmuQamPlugin(dev, ROG_ALLY_PP_MAP if pp_enable else None),
|
116
|
+
)
|
117
|
+
drivers_matched = True
|
118
|
+
|
119
|
+
if not drivers_matched:
|
105
120
|
for name, (dev, cpu) in CPU_DATA.items():
|
106
121
|
if name in cpuinfo:
|
107
|
-
drivers.append(SmuDriverPlugin(dev, cpu))
|
108
122
|
drivers.append(
|
109
|
-
|
123
|
+
SmuDriverPlugin(
|
110
124
|
dev,
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
125
|
+
cpu,
|
126
|
+
platform_profile=True,
|
127
|
+
)
|
128
|
+
)
|
129
|
+
drivers.append(
|
130
|
+
SmuQamPlugin(dev, ROG_ALLY_PP_MAP),
|
115
131
|
)
|
116
132
|
break
|
117
133
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
type: container
|
2
|
+
title: General Settings
|
3
|
+
tags: [ advanced ]
|
4
|
+
children:
|
5
|
+
enable:
|
6
|
+
title: Enable TDP Controls
|
7
|
+
type: bool
|
8
|
+
default: False
|
9
|
+
|
10
|
+
enforce_limits:
|
11
|
+
title: Enforce Device Limits
|
12
|
+
type: bool
|
13
|
+
tags: [ expert ]
|
14
|
+
default: True
|
15
|
+
|
16
|
+
error:
|
17
|
+
title: Error
|
18
|
+
type: display
|
19
|
+
tags: [error]
|
20
|
+
options:
|
21
|
+
nowrite: Can not write to ACPI Call file. ACPI Call is required for TDP.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: adjustor
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.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
|
@@ -35,22 +35,35 @@ As part of the latest Lenovo bios, it also allows for setting a custom fan curve
|
|
35
35
|
for the Legion Go.
|
36
36
|
|
37
37
|
## AMD TDP Control
|
38
|
-
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
39
|
-
which exposes a superset of the parameters that can be found in
|
38
|
+
Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
|
39
|
+
of AMD, which exposes a superset of the parameters that can be currently found in
|
40
40
|
[RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/), through ACPI.
|
41
|
-
This vendor
|
41
|
+
This vendor interface is part of the ACPI ASL library, and provided through the
|
42
42
|
ALIB method 0x0C.
|
43
|
+
The underlying implementation of the interface is SMU calls.
|
43
44
|
This means that as long as the kernel module `acpi_call` is loaded, Adjustor
|
44
45
|
can control TDP in an equivalent way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
|
45
46
|
|
46
|
-
|
47
|
-
|
47
|
+
Right now, Adjustor only implements a subset of useful ALIB parameters that are
|
48
|
+
well documented.
|
49
|
+
In addition, ALIB does not provide a way for reading the performance metrics table,
|
50
|
+
so Adjustor can only write (not read) TDP values.
|
48
51
|
From reverse engineering the Legion Go (see [here](./alib.md)), and seeing how it
|
49
52
|
interacts with ALIB, it was found that there are at least 10 parameters which control
|
50
53
|
the method STTv2 and are not part of RyzenAdj or have been documented elsewhere.
|
51
54
|
|
52
55
|
## Installation
|
53
|
-
|
56
|
+
Adjustor is available on [AUR](https://aur.archlinux.org/packages/adjustor)
|
57
|
+
and provided Handheld Daemon has been installed through
|
58
|
+
[AUR](https://aur.archlinux.org/packages/hhd) too, it will load it automatically
|
59
|
+
on restart.
|
60
|
+
COPR coming soon.
|
61
|
+
|
62
|
+
Alternatively, on a local install of Handheld Daemon you may:
|
63
|
+
```bash
|
64
|
+
~/.local/share/hhd/venv/bin/pip install --upgrade adjustor
|
65
|
+
```
|
66
|
+
However, the autoupdater in Handheld Daemon does not support updating yet.
|
54
67
|
|
55
68
|
## Development
|
56
69
|
Install to the same virtual environment as hhd to have Adjustor picked up
|
@@ -1,9 +1,11 @@
|
|
1
1
|
LICENSE
|
2
|
+
MANIFEST.in
|
2
3
|
pyproject.toml
|
3
4
|
readme.md
|
4
5
|
src/adjustor/__init__.py
|
5
6
|
src/adjustor/__main__.py
|
6
7
|
src/adjustor/hhd.py
|
8
|
+
src/adjustor/settings.yml
|
7
9
|
src/adjustor.egg-info/PKG-INFO
|
8
10
|
src/adjustor.egg-info/SOURCES.txt
|
9
11
|
src/adjustor.egg-info/dependency_links.txt
|
@@ -17,4 +19,7 @@ src/adjustor/core/const.py
|
|
17
19
|
src/adjustor/core/lenovo.py
|
18
20
|
src/adjustor/drivers/__init__.py
|
19
21
|
src/adjustor/drivers/lenovo/__init__.py
|
20
|
-
src/adjustor/drivers/
|
22
|
+
src/adjustor/drivers/lenovo/settings.yml
|
23
|
+
src/adjustor/drivers/smu/__init__.py
|
24
|
+
src/adjustor/drivers/smu/qam.yml
|
25
|
+
src/adjustor/drivers/smu/smu.yml
|
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
|