adjustor 3.5.1__tar.gz → 3.5.3__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {adjustor-3.5.1/src/adjustor.egg-info → adjustor-3.5.3}/PKG-INFO +1 -1
- {adjustor-3.5.1 → adjustor-3.5.3}/pyproject.toml +1 -1
- adjustor-3.5.3/src/adjustor/core/fan/__main__.py +7 -0
- adjustor-3.5.3/src/adjustor/core/fan/alg.py +157 -0
- adjustor-3.5.3/src/adjustor/core/fan/core.py +194 -0
- adjustor-3.5.3/src/adjustor/core/fan/utils.py +93 -0
- adjustor-3.5.3/src/adjustor/drivers/__init__.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/amd/__init__.py +51 -56
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/amd/settings.yml +54 -54
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/asus/__init__.py +0 -1
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/general/__init__.py +69 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/events.py +7 -3
- {adjustor-3.5.1 → adjustor-3.5.3/src/adjustor.egg-info}/PKG-INFO +1 -1
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor.egg-info/SOURCES.txt +5 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/LICENSE +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/MANIFEST.in +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/readme.md +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/setup.cfg +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/__init__.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/__main__.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/core/__init__.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/core/acpi.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/core/alib.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/core/const.py +0 -0
- {adjustor-3.5.1/src/adjustor/drivers → adjustor-3.5.3/src/adjustor/core/fan}/__init__.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/core/lenovo.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/core/platform.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/amd/power-profiles-daemon.dbus.xml.in +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/amd/ppd.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/asus/settings.yml +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/general/settings.yml +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/lenovo/__init__.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/lenovo/settings.yml +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/smu/__init__.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/smu/qam.yml +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/drivers/smu/smu.yml +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/fuse/__init__.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/fuse/driver.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/fuse/gpu.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/fuse/utils.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/hhd.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/i18n.py +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor/settings.yml +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor.egg-info/dependency_links.txt +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor.egg-info/entry_points.txt +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor.egg-info/requires.txt +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/src/adjustor.egg-info/top_level.txt +0 -0
- {adjustor-3.5.1 → adjustor-3.5.3}/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.5.
|
3
|
+
Version: 3.5.3
|
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
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# The objective of this fan algorithm is simple: receive a fan curve for a specific
|
2
|
+
# power profile, the current system temperature, and the current fan speed.
|
3
|
+
# Then, have a single variable memory: the current acceleration.
|
4
|
+
# At each point, use a derived jerk to modify the acceleration and move the
|
5
|
+
# speed to the desired point smoothly.
|
6
|
+
|
7
|
+
# Maximum typical transition span of the fan speed (e.g., from 20% to 70%)
|
8
|
+
# This transition span should take exactly the seconds specified below
|
9
|
+
# E.g., if the fan begins to move from 20% to 70% with 0 acceleration, where
|
10
|
+
# the temperature is at 70C, it should take at most 5 seconds.
|
11
|
+
SPEED_SPAN = 0.5
|
12
|
+
|
13
|
+
# The ratio of the curve to begin decelerating the fan change rpm/s^2
|
14
|
+
# E.g., when going from 20% to 70%, the fan should begin decelerating
|
15
|
+
# at 0.7*(70-20) + 20 = 62% speed.
|
16
|
+
DECEL_RATIO = 0.5
|
17
|
+
|
18
|
+
# The temperature at which the fan should use the high accel speed.
|
19
|
+
HIGH_TEMP_EDGE = 65
|
20
|
+
HIGH_TEMP_JUNCTION = 75
|
21
|
+
|
22
|
+
# Allow for a gradual transition if the temperature is low
|
23
|
+
# Speed up if not.
|
24
|
+
ACCEL_UP_LOWT_T = 30
|
25
|
+
ACCEL_UP_HIGH_T = 20
|
26
|
+
# Penalize going down to avoid dithering
|
27
|
+
ACCEL_DOWN_T = 40
|
28
|
+
|
29
|
+
JERK_TOLERANCE = 0.9
|
30
|
+
MAX_ACCEL = 0.4
|
31
|
+
SETPOINT_DEVIATION = 0.01
|
32
|
+
|
33
|
+
UPDATE_FREQUENCY = 5
|
34
|
+
UPDATE_T = 1 / UPDATE_FREQUENCY
|
35
|
+
SETPOINT_UPDATE_FREQUENCY = 1
|
36
|
+
SETPOINT_UPDATE_T = 1 / SETPOINT_UPDATE_FREQUENCY
|
37
|
+
HYSTERESIS_RATIO = 0.25
|
38
|
+
|
39
|
+
|
40
|
+
def _calculate_jerk(speed_span, decel_ratio, freq, time):
|
41
|
+
"""Calculate the required positive and negative jerks such that the
|
42
|
+
fan can cross the speed span (e.g., 50%) in the specified time (e.g., 5s)
|
43
|
+
given an update frequency (e.g., 3), and the point at which it should start
|
44
|
+
decelerating (e.g., 0.3).
|
45
|
+
"""
|
46
|
+
|
47
|
+
jerk_accel = (2 * speed_span) / ((time * freq) ** 2) / ((1 - decel_ratio) ** 2)
|
48
|
+
jerk_decel = -(1 - decel_ratio) / decel_ratio * jerk_accel
|
49
|
+
return jerk_accel, jerk_decel
|
50
|
+
|
51
|
+
|
52
|
+
def calculate_jerk(t_target: float, increase: bool, junction: bool):
|
53
|
+
"""Calculate the jerk based on the target temperature and whether the fan
|
54
|
+
speed should increase or decrease to reach it.
|
55
|
+
|
56
|
+
Allow for specifying whether the temperature probe is in the junction or the
|
57
|
+
edge, as junction reaches thermal saturation faster and at higher temperatures."""
|
58
|
+
if not increase:
|
59
|
+
return _calculate_jerk(SPEED_SPAN, DECEL_RATIO, UPDATE_FREQUENCY, ACCEL_DOWN_T)
|
60
|
+
|
61
|
+
if (junction and t_target > HIGH_TEMP_JUNCTION) or (
|
62
|
+
not junction and t_target > HIGH_TEMP_EDGE
|
63
|
+
):
|
64
|
+
return _calculate_jerk(
|
65
|
+
SPEED_SPAN, DECEL_RATIO, UPDATE_FREQUENCY, ACCEL_UP_HIGH_T
|
66
|
+
)
|
67
|
+
|
68
|
+
return _calculate_jerk(SPEED_SPAN, DECEL_RATIO, UPDATE_FREQUENCY, ACCEL_UP_LOWT_T)
|
69
|
+
|
70
|
+
|
71
|
+
def move_to_setpoint(v_curr, a_curr, jerk_accel, jerk_decel, v_target):
|
72
|
+
"""Update the current fan speed and acceleration by either using jerk_accel
|
73
|
+
which will increase acceleration to meet the target speed or jerk_decel
|
74
|
+
which will begin to decrease it to 0.
|
75
|
+
|
76
|
+
The choice between jerk_accel and jerk_decel is made by calculating the
|
77
|
+
minimum negative jerk required to decelerate to a=0 when reaching the target.
|
78
|
+
If the minimum jerk is smaller than jerk_decel, we use jerk_accel.
|
79
|
+
To avoid overshoots, a tolerance is used to start decelerating a bit earlier
|
80
|
+
than when the minimum jerk reaches the value of jerk_decel.
|
81
|
+
"""
|
82
|
+
|
83
|
+
# Flip the jerks if the target is lower than the current speed
|
84
|
+
diff = v_target - v_curr
|
85
|
+
if diff < 0:
|
86
|
+
jerk_accel = -jerk_accel
|
87
|
+
jerk_decel = -jerk_decel
|
88
|
+
|
89
|
+
correct_direction = (diff > 0 and a_curr > 0) or (diff < 0 and a_curr < 0)
|
90
|
+
non_zero = abs(diff) > 1e-3
|
91
|
+
|
92
|
+
# Always accelerate if we are on the right direction or speed is zero
|
93
|
+
# Start decelerating once we run out of opposite jerk.
|
94
|
+
accel = True
|
95
|
+
if correct_direction and non_zero:
|
96
|
+
min_jerk_neg = -(a_curr**2) / 2 / diff
|
97
|
+
accel = abs(min_jerk_neg) < JERK_TOLERANCE * abs(jerk_decel)
|
98
|
+
|
99
|
+
if accel:
|
100
|
+
jerk = jerk_accel
|
101
|
+
else:
|
102
|
+
jerk = jerk_decel
|
103
|
+
|
104
|
+
# Calculate the new acceleration
|
105
|
+
a_new = a_curr + jerk
|
106
|
+
v_new = v_curr + a_new
|
107
|
+
return v_new, a_new
|
108
|
+
|
109
|
+
|
110
|
+
def sanitize_fan_values(v: float, a: float):
|
111
|
+
return max(0, min(1, v)), max(-MAX_ACCEL, min(MAX_ACCEL, a))
|
112
|
+
|
113
|
+
|
114
|
+
def has_reached_setpoint(v_curr, a_curr, v_target):
|
115
|
+
"""Check if the current fan speed has reached the target speed.
|
116
|
+
|
117
|
+
If true, lower the update rate and set a_curr to 0 to avoid dithering.
|
118
|
+
"""
|
119
|
+
return abs(v_curr - v_target) < SETPOINT_DEVIATION
|
120
|
+
|
121
|
+
|
122
|
+
def update_setpoint(temp: float, curr: int, fan_curve: dict[int, float]):
|
123
|
+
"""Update the setpoint given the current temperature, fan curve, and previous setpoint.
|
124
|
+
|
125
|
+
Fan curve is a dictionary of increasing temperatures to fan speeds.
|
126
|
+
"""
|
127
|
+
|
128
|
+
targets = list(fan_curve.keys())
|
129
|
+
assert curr in targets, "Current setpoint not in fan curve"
|
130
|
+
|
131
|
+
idx = targets.index(curr)
|
132
|
+
|
133
|
+
# Add some hysterisis to avoid dithering
|
134
|
+
if idx > 0:
|
135
|
+
prev = targets[idx - 1]
|
136
|
+
if temp < prev + (curr - prev) * HYSTERESIS_RATIO:
|
137
|
+
return prev
|
138
|
+
|
139
|
+
if idx < len(targets) - 1:
|
140
|
+
next = targets[idx + 1]
|
141
|
+
if temp > next - (next - curr) * HYSTERESIS_RATIO:
|
142
|
+
return next
|
143
|
+
|
144
|
+
return curr
|
145
|
+
|
146
|
+
|
147
|
+
def get_initial_setpoint(temp: float, fan_curve: dict[int, float]):
|
148
|
+
"""Get the initial setpoint given the current temperature and fan curve.
|
149
|
+
|
150
|
+
Fan curve is a dictionary of increasing temperatures to fan speeds.
|
151
|
+
"""
|
152
|
+
|
153
|
+
targets = list(fan_curve.keys())
|
154
|
+
for idx, target in enumerate(targets):
|
155
|
+
if temp < target:
|
156
|
+
return targets[idx - 1] if idx else targets[0]
|
157
|
+
return targets[-1]
|
@@ -0,0 +1,194 @@
|
|
1
|
+
import logging
|
2
|
+
import time
|
3
|
+
from typing import TypedDict
|
4
|
+
|
5
|
+
from .alg import (
|
6
|
+
SETPOINT_UPDATE_T,
|
7
|
+
UPDATE_T,
|
8
|
+
calculate_jerk,
|
9
|
+
get_initial_setpoint,
|
10
|
+
has_reached_setpoint,
|
11
|
+
move_to_setpoint,
|
12
|
+
sanitize_fan_values,
|
13
|
+
update_setpoint,
|
14
|
+
)
|
15
|
+
from .utils import (
|
16
|
+
find_edge_temp,
|
17
|
+
find_fans,
|
18
|
+
find_tctl_temp,
|
19
|
+
read_fan_speed,
|
20
|
+
read_temp,
|
21
|
+
write_fan_speed,
|
22
|
+
)
|
23
|
+
|
24
|
+
logger = logging.getLogger(__name__)
|
25
|
+
|
26
|
+
|
27
|
+
class FanInfo(TypedDict):
|
28
|
+
tctl: str
|
29
|
+
edge: str
|
30
|
+
fans: list[str]
|
31
|
+
|
32
|
+
|
33
|
+
class FanData(TypedDict):
|
34
|
+
a: float
|
35
|
+
v: float
|
36
|
+
t_target: int
|
37
|
+
|
38
|
+
|
39
|
+
class FanState(TypedDict):
|
40
|
+
v_curr: float
|
41
|
+
v_target: float
|
42
|
+
v_target_pwm: int
|
43
|
+
v_rpm: list[int]
|
44
|
+
t_junction: float
|
45
|
+
t_edge: float
|
46
|
+
fan_data: FanData
|
47
|
+
|
48
|
+
|
49
|
+
def get_fan_info() -> FanInfo | None:
|
50
|
+
tctl = find_tctl_temp()
|
51
|
+
if tctl is None:
|
52
|
+
logger.error("Could not find tctl junction temperature.")
|
53
|
+
return None
|
54
|
+
|
55
|
+
edge = find_edge_temp()
|
56
|
+
if edge is None:
|
57
|
+
logger.error("Could not find edge temperature.")
|
58
|
+
return None
|
59
|
+
|
60
|
+
fans = find_fans()
|
61
|
+
if not fans:
|
62
|
+
logger.error("Could not find PWM controllable fans.")
|
63
|
+
return None
|
64
|
+
|
65
|
+
return {"tctl": tctl, "edge": edge, "fans": fans}
|
66
|
+
|
67
|
+
|
68
|
+
def calculate_fan_speed(
|
69
|
+
temp: float, data: FanData | None, fan_curve: dict[int, float], junction: bool
|
70
|
+
) -> tuple[float, bool, FanData]:
|
71
|
+
if data is None:
|
72
|
+
# Initialize with best guess
|
73
|
+
t_target = get_initial_setpoint(temp, fan_curve)
|
74
|
+
v_curr = fan_curve[t_target]
|
75
|
+
a_curr = 0
|
76
|
+
return v_curr, False, {"a": a_curr, "v": v_curr, "t_target": t_target}
|
77
|
+
|
78
|
+
# Get values and new temp target setpoint
|
79
|
+
v_curr = data["v"]
|
80
|
+
a_curr = data["a"]
|
81
|
+
t_target = data["t_target"]
|
82
|
+
t_target = update_setpoint(temp, t_target, fan_curve)
|
83
|
+
|
84
|
+
# Pin values if we are in the setpoint
|
85
|
+
if has_reached_setpoint(v_curr, a_curr, fan_curve[t_target]):
|
86
|
+
a_curr = 0
|
87
|
+
v_curr = fan_curve[t_target]
|
88
|
+
return v_curr, True, {"a": a_curr, "v": v_curr, "t_target": t_target}
|
89
|
+
|
90
|
+
v_target = fan_curve[t_target]
|
91
|
+
jerk_accel, jerk_decel = calculate_jerk(t_target, v_target > v_curr, junction)
|
92
|
+
v_new, a_new = move_to_setpoint(v_curr, a_curr, jerk_accel, jerk_decel, v_target)
|
93
|
+
v_new, a_new = sanitize_fan_values(v_new, a_new)
|
94
|
+
|
95
|
+
return v_new, False, {"a": a_new, "v": v_new, "t_target": t_target}
|
96
|
+
|
97
|
+
|
98
|
+
def set_fans_to_pwm(enable: bool, fan_info: FanInfo):
|
99
|
+
for _, fn_enable, _ in fan_info["fans"]:
|
100
|
+
with open(fn_enable, "w") as f:
|
101
|
+
f.write("1" if enable else "0")
|
102
|
+
|
103
|
+
|
104
|
+
def update_fan_speed(
|
105
|
+
state: FanState | None,
|
106
|
+
fan_info: FanInfo,
|
107
|
+
fan_curve: dict[int, float],
|
108
|
+
junction: bool,
|
109
|
+
observe_only: bool = False,
|
110
|
+
) -> tuple[bool, FanState]:
|
111
|
+
t_edge = read_temp(fan_info["edge"])
|
112
|
+
t_junction = read_temp(fan_info["tctl"])
|
113
|
+
|
114
|
+
t_curr = t_junction if junction else t_edge
|
115
|
+
data = state["fan_data"] if state else None
|
116
|
+
v_curr, in_setpoint, data = calculate_fan_speed(t_curr, data, fan_curve, junction)
|
117
|
+
|
118
|
+
v_curr_int = min(255, max(0, int(v_curr * 255)))
|
119
|
+
if not observe_only:
|
120
|
+
if state is None or state["v_target_pwm"] != v_curr_int:
|
121
|
+
for v_fn, _, _ in fan_info["fans"]:
|
122
|
+
write_fan_speed(v_fn, v_curr_int)
|
123
|
+
|
124
|
+
fan_speeds = [read_fan_speed(rpm_fn) for _, _, rpm_fn in fan_info["fans"] if rpm_fn]
|
125
|
+
return (
|
126
|
+
in_setpoint,
|
127
|
+
{
|
128
|
+
"v_curr": v_curr,
|
129
|
+
"v_target": fan_curve[data["t_target"]],
|
130
|
+
"v_target_pwm": v_curr_int,
|
131
|
+
"v_rpm": fan_speeds,
|
132
|
+
"t_junction": t_junction,
|
133
|
+
"t_edge": t_edge,
|
134
|
+
"fan_data": data,
|
135
|
+
},
|
136
|
+
)
|
137
|
+
|
138
|
+
|
139
|
+
def fan_pwm_tester(normal_curve: bool = True, observe_only: bool = False):
|
140
|
+
fan_info = get_fan_info()
|
141
|
+
if fan_info is None:
|
142
|
+
return
|
143
|
+
|
144
|
+
if normal_curve:
|
145
|
+
fan_curve = {
|
146
|
+
50: 0.3,
|
147
|
+
60: 0.35,
|
148
|
+
70: 0.4,
|
149
|
+
80: 0.5,
|
150
|
+
85: 0.6,
|
151
|
+
90: 0.8,
|
152
|
+
100: 0.9,
|
153
|
+
}
|
154
|
+
fan_curve = {
|
155
|
+
40: 0.2,
|
156
|
+
45: 0.3,
|
157
|
+
50: 0.4,
|
158
|
+
55: 0.45,
|
159
|
+
60: 0.55,
|
160
|
+
65: 0.7,
|
161
|
+
70: 0.8,
|
162
|
+
80: 0.85,
|
163
|
+
90: 0.9,
|
164
|
+
100: 1,
|
165
|
+
}
|
166
|
+
else:
|
167
|
+
fan_curve = {
|
168
|
+
50: 0.3,
|
169
|
+
60: 0.3,
|
170
|
+
65: 0.9,
|
171
|
+
80: 0.9,
|
172
|
+
90: 0.9,
|
173
|
+
100: 1,
|
174
|
+
}
|
175
|
+
|
176
|
+
try:
|
177
|
+
if not observe_only:
|
178
|
+
set_fans_to_pwm(True, fan_info)
|
179
|
+
|
180
|
+
MAX_FAN = 5300
|
181
|
+
|
182
|
+
state = None
|
183
|
+
for i in range(10000000):
|
184
|
+
in_setpoint, state = update_fan_speed(state, fan_info, fan_curve, False, observe_only=observe_only)
|
185
|
+
|
186
|
+
print(f"\n> {i:05d}: {'in setpoint' if in_setpoint else 'updating'}{' (observe)' if observe_only else ''}")
|
187
|
+
print(f" Junction: {state['t_junction']:.2f}C, Edge: {state['t_edge']:.2f}C")
|
188
|
+
print(f" Current: {state['v_curr']*100:.1f}%, Target: {state['v_target']*100:.1f}%")
|
189
|
+
print(f" Fan speeds: {' '.join(map(lambda rpm: f"{rpm:4d}rpm/{MAX_FAN}rpm ({100*rpm/MAX_FAN:.1f}%)", state['v_rpm']))}")
|
190
|
+
time.sleep(SETPOINT_UPDATE_T if in_setpoint else UPDATE_T)
|
191
|
+
except KeyboardInterrupt:
|
192
|
+
print("Exiting fan test.")
|
193
|
+
finally:
|
194
|
+
set_fans_to_pwm(False, fan_info)
|
@@ -0,0 +1,93 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
FAN_HWMONS = ["oxpec"]
|
4
|
+
HWMON_DIR = "/sys/class/hwmon"
|
5
|
+
|
6
|
+
|
7
|
+
def get_hwmon():
|
8
|
+
for dir in os.listdir(HWMON_DIR):
|
9
|
+
if dir.startswith("hwmon"):
|
10
|
+
yield dir
|
11
|
+
|
12
|
+
|
13
|
+
def find_edge_temp():
|
14
|
+
for hwmon in get_hwmon():
|
15
|
+
with open(f"{HWMON_DIR}/{hwmon}/name") as f:
|
16
|
+
name = f.read().strip()
|
17
|
+
|
18
|
+
if name != "amdgpu":
|
19
|
+
continue
|
20
|
+
|
21
|
+
# For sanity, check the device has CPUs to avoid hooking an eGPU.
|
22
|
+
if not os.path.exists(f"{HWMON_DIR}/{hwmon}/device/local_cpus"):
|
23
|
+
continue
|
24
|
+
|
25
|
+
if not os.path.exists(f"{HWMON_DIR}/{hwmon}/temp1_input"):
|
26
|
+
continue
|
27
|
+
|
28
|
+
return f"{HWMON_DIR}/{hwmon}/temp1_input"
|
29
|
+
|
30
|
+
|
31
|
+
def find_tctl_temp():
|
32
|
+
for hwmon in get_hwmon():
|
33
|
+
with open(f"{HWMON_DIR}/{hwmon}/name") as f:
|
34
|
+
name = f.read().strip()
|
35
|
+
|
36
|
+
if name != "k10temp":
|
37
|
+
continue
|
38
|
+
|
39
|
+
# For sanity, check the device has CPUs to avoid hooking an eGPU.
|
40
|
+
if not os.path.exists(f"{HWMON_DIR}/{hwmon}/device/local_cpus"):
|
41
|
+
continue
|
42
|
+
|
43
|
+
if not os.path.exists(f"{HWMON_DIR}/{hwmon}/temp1_input"):
|
44
|
+
continue
|
45
|
+
|
46
|
+
return f"{HWMON_DIR}/{hwmon}/temp1_input"
|
47
|
+
|
48
|
+
|
49
|
+
def find_fans():
|
50
|
+
"""Finds tunable fans with endpoints pwmX and pwmX_enable."""
|
51
|
+
fans = []
|
52
|
+
for hwmon in get_hwmon():
|
53
|
+
with open(f"{HWMON_DIR}/{hwmon}/name") as f:
|
54
|
+
name = f.read().strip()
|
55
|
+
|
56
|
+
if name not in FAN_HWMONS:
|
57
|
+
continue
|
58
|
+
|
59
|
+
for fn in os.listdir(f"{HWMON_DIR}/{hwmon}"):
|
60
|
+
if (
|
61
|
+
fn.startswith("pwm")
|
62
|
+
and fn[3:].isdigit()
|
63
|
+
and os.path.exists(f"{HWMON_DIR}/{hwmon}/{fn}_enable")
|
64
|
+
):
|
65
|
+
idx = fn[3:]
|
66
|
+
speed = f"fan{idx}_input"
|
67
|
+
if speed in os.listdir(f"{HWMON_DIR}/{hwmon}"):
|
68
|
+
speed_fn = f"{HWMON_DIR}/{hwmon}/{speed}"
|
69
|
+
else:
|
70
|
+
speed_fn = None
|
71
|
+
fans.append(
|
72
|
+
(
|
73
|
+
f"{HWMON_DIR}/{hwmon}/{fn}",
|
74
|
+
f"{HWMON_DIR}/{hwmon}/{fn}_enable",
|
75
|
+
speed_fn,
|
76
|
+
)
|
77
|
+
)
|
78
|
+
|
79
|
+
return fans
|
80
|
+
|
81
|
+
|
82
|
+
def read_temp(path: str) -> float:
|
83
|
+
with open(path, "r") as f:
|
84
|
+
return int(f.read()) / 1000
|
85
|
+
|
86
|
+
|
87
|
+
def read_fan_speed(path: str) -> int:
|
88
|
+
with open(path, "r") as f:
|
89
|
+
return int(f.read())
|
90
|
+
|
91
|
+
def write_fan_speed(path: str, speed: int):
|
92
|
+
with open(path, "w") as f:
|
93
|
+
f.write(str(speed))
|
File without changes
|
@@ -142,11 +142,13 @@ class AmdGPUPlugin(HHDPlugin):
|
|
142
142
|
|
143
143
|
if self.ppd_conflict and os.environ.get("HHD_PPD_MASK", None):
|
144
144
|
logger.warning(
|
145
|
-
"PPD conflict detected but HHD_PPD_MASK is set. Masking PPD."
|
145
|
+
"PPD conflict detected but HHD_PPD_MASK is set. Masking PPD/TuneD."
|
146
146
|
)
|
147
147
|
# Mask and disable
|
148
148
|
os.system("systemctl mask power-profiles-daemon.service")
|
149
149
|
os.system("systemctl disable --now power-profiles-daemon.service")
|
150
|
+
os.system("systemctl mask tuned.service")
|
151
|
+
os.system("systemctl disable --now tuned.service")
|
150
152
|
# Keep going without check to avoid obscure errors
|
151
153
|
self.ppd_conflict = False
|
152
154
|
|
@@ -158,20 +160,20 @@ class AmdGPUPlugin(HHDPlugin):
|
|
158
160
|
}
|
159
161
|
|
160
162
|
self.initialized = True
|
161
|
-
|
163
|
+
|
162
164
|
# Initialize frequency settings
|
163
|
-
manual_freq = sets["enabled"]["children"]["
|
165
|
+
manual_freq = sets["enabled"]["children"]["gpu_freq"]["modes"]["manual"][
|
166
|
+
"children"
|
167
|
+
]["frequency"]
|
168
|
+
upper_freq = sets["enabled"]["children"]["gpu_freq"]["modes"]["upper"][
|
164
169
|
"children"
|
165
|
-
]["
|
166
|
-
|
170
|
+
]["frequency"]
|
171
|
+
min_freq = sets["enabled"]["children"]["gpu_freq"]["modes"]["range"][
|
167
172
|
"children"
|
168
|
-
]["
|
169
|
-
|
170
|
-
"
|
171
|
-
]["
|
172
|
-
max_freq = sets["enabled"]["children"]["mode"]["modes"]["manual"]["children"][
|
173
|
-
"gpu_freq"
|
174
|
-
]["modes"]["range"]["children"]["max"]
|
173
|
+
]["min"]
|
174
|
+
max_freq = sets["enabled"]["children"]["gpu_freq"]["modes"]["range"][
|
175
|
+
"children"
|
176
|
+
]["max"]
|
175
177
|
|
176
178
|
manual_freq["default"] = ((status.freq_min + status.freq_max) // 200) * 100
|
177
179
|
upper_freq["default"] = status.freq_max
|
@@ -285,6 +287,10 @@ class AmdGPUPlugin(HHDPlugin):
|
|
285
287
|
if new_ppd:
|
286
288
|
try:
|
287
289
|
self.proc, self.t = _open_ppd_server(self.emit)
|
290
|
+
# Fixup target in case it came before
|
291
|
+
if self.proc.stdin and self.target:
|
292
|
+
self.proc.stdin.write(f"{self.target}\n".encode())
|
293
|
+
self.proc.stdin.flush()
|
288
294
|
except Exception as e:
|
289
295
|
logger.error(f"Failed to open PPD server:\n{e}")
|
290
296
|
self.close_ppd()
|
@@ -305,7 +311,6 @@ class AmdGPUPlugin(HHDPlugin):
|
|
305
311
|
try:
|
306
312
|
match self.target:
|
307
313
|
case "balanced":
|
308
|
-
set_gpu_auto()
|
309
314
|
if self.supports_epp:
|
310
315
|
set_powersave_governor()
|
311
316
|
set_epp_mode("balance_power")
|
@@ -313,7 +318,6 @@ class AmdGPUPlugin(HHDPlugin):
|
|
313
318
|
set_cpu_boost(True)
|
314
319
|
set_frequency_scaling(nonlinear=False)
|
315
320
|
case "performance":
|
316
|
-
set_gpu_auto()
|
317
321
|
if self.supports_epp:
|
318
322
|
set_powersave_governor()
|
319
323
|
set_epp_mode("balance_power")
|
@@ -321,7 +325,6 @@ class AmdGPUPlugin(HHDPlugin):
|
|
321
325
|
set_cpu_boost(True)
|
322
326
|
set_frequency_scaling(nonlinear=True)
|
323
327
|
case _: # power
|
324
|
-
set_gpu_auto()
|
325
328
|
if self.supports_epp:
|
326
329
|
set_powersave_governor()
|
327
330
|
set_epp_mode("power")
|
@@ -334,52 +337,11 @@ class AmdGPUPlugin(HHDPlugin):
|
|
334
337
|
# Unless it is set manually, use the default scheduler.
|
335
338
|
self.close_sched()
|
336
339
|
self.old_sched = None
|
337
|
-
self.old_freq = None
|
338
340
|
self.old_boost = None
|
339
341
|
self.old_epp = None
|
340
342
|
self.old_min_freq = None
|
341
343
|
else:
|
342
344
|
self.old_target = None
|
343
|
-
new_gpu = conf["tdp.amd_energy.mode.manual.gpu_freq.mode"].to(str)
|
344
|
-
match new_gpu:
|
345
|
-
case "manual":
|
346
|
-
f = conf["tdp.amd_energy.mode.manual.gpu_freq.manual.frequency"].to(
|
347
|
-
int
|
348
|
-
)
|
349
|
-
new_freq = (f, f)
|
350
|
-
case "upper":
|
351
|
-
f = conf["tdp.amd_energy.mode.manual.gpu_freq.upper.frequency"].to(
|
352
|
-
int
|
353
|
-
)
|
354
|
-
new_freq = (self.min_freq or f, f)
|
355
|
-
case "range":
|
356
|
-
min_f = conf["tdp.amd_energy.mode.manual.gpu_freq.range.min"].to(
|
357
|
-
int
|
358
|
-
)
|
359
|
-
max_f = conf["tdp.amd_energy.mode.manual.gpu_freq.range.max"].to(
|
360
|
-
int
|
361
|
-
)
|
362
|
-
if max_f < min_f:
|
363
|
-
max_f = min_f
|
364
|
-
conf["tdp.amd_energy.mode.manual.gpu_freq.range.max"] = min_f
|
365
|
-
new_freq = (min_f, max_f)
|
366
|
-
case _:
|
367
|
-
new_freq = None
|
368
|
-
|
369
|
-
if new_freq != self.old_freq:
|
370
|
-
self.old_freq = new_freq
|
371
|
-
self.queue_gpu = curr + APPLY_DELAY
|
372
|
-
|
373
|
-
if self.queue_gpu is not None and curr >= self.queue_gpu:
|
374
|
-
self.queue_gpu = None
|
375
|
-
try:
|
376
|
-
if new_freq:
|
377
|
-
set_gpu_manual(*new_freq)
|
378
|
-
else:
|
379
|
-
set_gpu_auto()
|
380
|
-
except Exception as e:
|
381
|
-
logger.error(f"Failed to set GPU mode:\n{e}")
|
382
|
-
|
383
345
|
if self.supports_boost:
|
384
346
|
new_boost = conf["tdp.amd_energy.mode.manual.cpu_boost"].to(bool)
|
385
347
|
if new_boost != self.old_boost:
|
@@ -439,6 +401,39 @@ class AmdGPUPlugin(HHDPlugin):
|
|
439
401
|
stdout=subprocess.DEVNULL,
|
440
402
|
)
|
441
403
|
|
404
|
+
# Apply GPU settings
|
405
|
+
new_gpu = conf["tdp.amd_energy.gpu_freq.mode"].to(str)
|
406
|
+
match new_gpu:
|
407
|
+
case "manual":
|
408
|
+
f = conf["tdp.amd_energy.gpu_freq.manual.frequency"].to(int)
|
409
|
+
new_freq = (f, f)
|
410
|
+
case "upper":
|
411
|
+
f = conf["tdp.amd_energy.gpu_freq.upper.frequency"].to(int)
|
412
|
+
new_freq = (self.min_freq or f, f)
|
413
|
+
case "range":
|
414
|
+
min_f = conf["tdp.amd_energy.gpu_freq.range.min"].to(int)
|
415
|
+
max_f = conf["tdp.amd_energy.gpu_freq.range.max"].to(int)
|
416
|
+
if max_f < min_f:
|
417
|
+
max_f = min_f
|
418
|
+
conf["tdp.amd_energy.gpu_freq.range.max"] = min_f
|
419
|
+
new_freq = (min_f, max_f)
|
420
|
+
case _:
|
421
|
+
new_freq = None
|
422
|
+
|
423
|
+
if new_freq != self.old_freq:
|
424
|
+
self.old_freq = new_freq
|
425
|
+
self.queue_gpu = curr + APPLY_DELAY
|
426
|
+
|
427
|
+
if self.queue_gpu is not None and curr >= self.queue_gpu:
|
428
|
+
self.queue_gpu = None
|
429
|
+
try:
|
430
|
+
if new_freq:
|
431
|
+
set_gpu_manual(*new_freq)
|
432
|
+
else:
|
433
|
+
set_gpu_auto()
|
434
|
+
except Exception as e:
|
435
|
+
logger.error(f"Failed to set GPU mode:\n{e}")
|
436
|
+
|
442
437
|
def close_ppd(self):
|
443
438
|
if self.proc is not None:
|
444
439
|
self.proc.send_signal(signal.SIGINT)
|
@@ -1,11 +1,11 @@
|
|
1
1
|
enabled:
|
2
|
-
title:
|
2
|
+
title: Processor Settings
|
3
3
|
type: container
|
4
4
|
tags: [ hide-title, non-essential ]
|
5
5
|
children:
|
6
6
|
mode:
|
7
7
|
type: mode
|
8
|
-
title:
|
8
|
+
title: CPU Settings
|
9
9
|
default: auto
|
10
10
|
modes:
|
11
11
|
auto:
|
@@ -63,56 +63,6 @@ enabled:
|
|
63
63
|
options:
|
64
64
|
disabled: Disabled
|
65
65
|
enabled: Enabled
|
66
|
-
gpu_freq:
|
67
|
-
type: mode
|
68
|
-
title: GPU Frequency
|
69
|
-
hint: >-
|
70
|
-
Pins the GPU to a certain frequency.
|
71
|
-
Helps in certain games that are CPU or GPU heavy
|
72
|
-
by shifting power to or from the GPU.
|
73
|
-
Has a minor effect.
|
74
|
-
default: auto
|
75
|
-
modes:
|
76
|
-
auto:
|
77
|
-
type: container
|
78
|
-
title: Auto
|
79
|
-
hint: >-
|
80
|
-
Lets the GPU manage its own frequency.
|
81
|
-
upper:
|
82
|
-
type: container
|
83
|
-
title: Upper Limit
|
84
|
-
hint: >-
|
85
|
-
Sets the GPU frequency to a maximum.
|
86
|
-
children:
|
87
|
-
frequency: &freq
|
88
|
-
type: int
|
89
|
-
unit: MHz
|
90
|
-
min: 300
|
91
|
-
max: 2000
|
92
|
-
step: 100
|
93
|
-
default: 1000
|
94
|
-
title: Maximum Frequency
|
95
|
-
range:
|
96
|
-
type: container
|
97
|
-
title: Range
|
98
|
-
hint: >-
|
99
|
-
Sets the GPU frequency to a range.
|
100
|
-
children:
|
101
|
-
min:
|
102
|
-
<<: *freq
|
103
|
-
title: Minimum Frequency
|
104
|
-
max:
|
105
|
-
<<: *freq
|
106
|
-
title: Maximum Frequency
|
107
|
-
manual:
|
108
|
-
type: container
|
109
|
-
title: Fixed
|
110
|
-
hint: >-
|
111
|
-
Sets the GPU frequency manually.
|
112
|
-
children:
|
113
|
-
frequency:
|
114
|
-
<<: *freq
|
115
|
-
title: Frequency
|
116
66
|
sched:
|
117
67
|
type: multiple
|
118
68
|
title: Custom Scheduler
|
@@ -125,9 +75,59 @@ enabled:
|
|
125
75
|
scx_bpfland: bpfland
|
126
76
|
scx_rusty: rusty
|
127
77
|
default: disabled
|
78
|
+
gpu_freq:
|
79
|
+
type: mode
|
80
|
+
title: GPU Frequency
|
81
|
+
hint: >-
|
82
|
+
Pins the GPU to a certain frequency.
|
83
|
+
Helps in certain games that are CPU or GPU heavy
|
84
|
+
by shifting power to or from the GPU.
|
85
|
+
Has a minor effect.
|
86
|
+
default: auto
|
87
|
+
modes:
|
88
|
+
auto:
|
89
|
+
type: container
|
90
|
+
title: Auto
|
91
|
+
hint: >-
|
92
|
+
Lets the GPU manage its own frequency.
|
93
|
+
upper:
|
94
|
+
type: container
|
95
|
+
title: Max Limit
|
96
|
+
hint: >-
|
97
|
+
Limits the maximum frequency of the GPU.
|
98
|
+
children:
|
99
|
+
frequency: &freq
|
100
|
+
type: int
|
101
|
+
unit: MHz
|
102
|
+
min: 300
|
103
|
+
max: 2000
|
104
|
+
step: 100
|
105
|
+
default: 1000
|
106
|
+
title: Maximum Frequency
|
107
|
+
range:
|
108
|
+
type: container
|
109
|
+
title: Range
|
110
|
+
hint: >-
|
111
|
+
Sets the GPU frequency to a range.
|
112
|
+
children:
|
113
|
+
min:
|
114
|
+
<<: *freq
|
115
|
+
title: Minimum Frequency
|
116
|
+
max:
|
117
|
+
<<: *freq
|
118
|
+
title: Maximum Frequency
|
119
|
+
manual:
|
120
|
+
type: container
|
121
|
+
title: Fixed
|
122
|
+
hint: >-
|
123
|
+
Pins the GPU to a certain frequency (not recommended).
|
124
|
+
children:
|
125
|
+
frequency:
|
126
|
+
<<: *freq
|
127
|
+
title: Frequency
|
128
128
|
|
129
129
|
conflict:
|
130
|
-
title:
|
130
|
+
title: Processor Settings
|
131
131
|
type: container
|
132
132
|
tags: [ hide-title, non-essential ]
|
133
133
|
children:
|
@@ -138,7 +138,7 @@ conflict:
|
|
138
138
|
Energy Management can not be enabled while PPD or TuneD are enabled.
|
139
139
|
`systemctl mask power-profiles-daemon` or `tuned`.
|
140
140
|
enable:
|
141
|
-
title: Enable
|
141
|
+
title: Enable Processor Settings
|
142
142
|
type: action
|
143
143
|
tags: [ non-essential ]
|
144
144
|
core:
|
@@ -27,6 +27,7 @@ class GeneralPowerPlugin(HHDPlugin):
|
|
27
27
|
self.old_sched = None
|
28
28
|
self.sched_proc = None
|
29
29
|
self.ppd_supported = None
|
30
|
+
self.tuned_supported = None
|
30
31
|
self.is_steamdeck = is_steamdeck
|
31
32
|
self.ovr_enabled = False
|
32
33
|
self.should_exit = Event()
|
@@ -54,6 +55,26 @@ class GeneralPowerPlugin(HHDPlugin):
|
|
54
55
|
except Exception as e:
|
55
56
|
logger.warning(f"powerprofilectl returned with error:\n{e}")
|
56
57
|
|
58
|
+
# TuneD
|
59
|
+
if self.tuned_supported is None:
|
60
|
+
self.tuned_supported = False
|
61
|
+
if tuned := shutil.which('tuned-adm'):
|
62
|
+
try:
|
63
|
+
if os.environ.get("HHD_PPD_MASK", None):
|
64
|
+
logger.info("Unmasking TuneD in the case it was masked.")
|
65
|
+
os.system('systemctl unmask tuned')
|
66
|
+
subprocess.run(
|
67
|
+
[tuned],
|
68
|
+
check=True,
|
69
|
+
stdin=subprocess.DEVNULL,
|
70
|
+
stdout=subprocess.DEVNULL,
|
71
|
+
stderr=subprocess.DEVNULL,
|
72
|
+
)
|
73
|
+
self.tuned_supported = True
|
74
|
+
except Exception as e:
|
75
|
+
logger.warning(f"tuned-adm returned with error:\n{e}")
|
76
|
+
|
77
|
+
|
57
78
|
if not self.ppd_supported:
|
58
79
|
del sets["children"]["profile"]
|
59
80
|
|
@@ -122,6 +143,54 @@ class GeneralPowerPlugin(HHDPlugin):
|
|
122
143
|
logger.warning(f"powerprofilectl returned with error:\n{e}")
|
123
144
|
self.ppd_supported = False
|
124
145
|
|
146
|
+
# Handle TuneD
|
147
|
+
if self.tuned_supported:
|
148
|
+
curr = time.time()
|
149
|
+
ppd_tuned_mapping = {
|
150
|
+
"power-saver": "powersave",
|
151
|
+
"balanced": "balanced",
|
152
|
+
"performance": "throughput-performance"
|
153
|
+
}
|
154
|
+
new_profile = ppd_tuned_mapping.get(conf.get("tdp.general.profile", self.target))
|
155
|
+
if new_profile != self.target and new_profile and self.target:
|
156
|
+
logger.info(f"Setting TuneD profile to '{new_profile}'")
|
157
|
+
self.target = new_profile
|
158
|
+
try:
|
159
|
+
subprocess.run(
|
160
|
+
[shutil.which('tuned-adm'), "profile", new_profile],
|
161
|
+
check=True,
|
162
|
+
stdout=subprocess.PIPE,
|
163
|
+
stderr=subprocess.PIPE,
|
164
|
+
)
|
165
|
+
except Exception as e:
|
166
|
+
self.tuned_supported = False
|
167
|
+
logger.warning(f"tuned-adm returned with error:\n{e}")
|
168
|
+
self.tuned_supported = False
|
169
|
+
elif not self.last_check or curr - self.last_check > 2:
|
170
|
+
# Update profile every 2 seconds
|
171
|
+
self.last_check = curr
|
172
|
+
try:
|
173
|
+
res = subprocess.run(
|
174
|
+
[shutil.which('tuned-adm'), "active"],
|
175
|
+
check=True,
|
176
|
+
stdout=subprocess.PIPE,
|
177
|
+
stderr=subprocess.PIPE,
|
178
|
+
)
|
179
|
+
|
180
|
+
tuned_ppd_mapping = {
|
181
|
+
"powersave": "power-saver",
|
182
|
+
"balanced": "balanced",
|
183
|
+
"throughput-performance": "performance"
|
184
|
+
}
|
185
|
+
self.target = tuned_ppd_mapping.get(res.stdout.decode().split(":")[1].strip()) # type: ignore
|
186
|
+
|
187
|
+
if self.target != conf["tdp.general.profile"].to(str):
|
188
|
+
conf["tdp.general.profile"] = self.target
|
189
|
+
except Exception as e:
|
190
|
+
self.tuned_supported = False
|
191
|
+
logger.warning(f"powerprofilectl returned with error:\n{e}")
|
192
|
+
self.tuned_supported = False
|
193
|
+
|
125
194
|
# Handle sched
|
126
195
|
if self.avail_scheds:
|
127
196
|
# Check health and print error
|
@@ -12,8 +12,12 @@ EVENT_MATCHES: Sequence[tuple[dict[str, Any], str]] = [
|
|
12
12
|
({"device_class": b"ac_adapter", "data": 0}, "dc"),
|
13
13
|
({"device_class": b"ac_adapter", "data": 256}, "ac"),
|
14
14
|
({"device_class": b"battery"}, "battery"),
|
15
|
+
({"device_class": b"button/power"}, "powerbutton"),
|
15
16
|
# Legion GO TDP event
|
16
17
|
({"bus_id": b"D320289E-8FEA-"}, "tdp"),
|
18
|
+
# GPD Force hibernate thermal event
|
19
|
+
# , 'type': 0xf100, 'data': 0x0100 ignore these attrs for now...
|
20
|
+
({"device_class": b"thermal_zone", "bus_id": b"LNXTHERM:00"}, "hibernate-thermal"),
|
17
21
|
]
|
18
22
|
|
19
23
|
GUARD_DELAY = 0.5
|
@@ -43,10 +47,10 @@ def loop_process_events(emit: Emitter, should_exit: TEvent):
|
|
43
47
|
break
|
44
48
|
|
45
49
|
if matches:
|
46
|
-
if etype
|
47
|
-
emit({"type": "acpi", "event": etype})
|
50
|
+
if etype not in ("battery", "powerbutton"):
|
51
|
+
emit({"type": "acpi", "event": etype}) # type: ignore
|
48
52
|
found = True
|
49
53
|
break
|
50
54
|
|
51
55
|
if not found:
|
52
|
-
logger.info(f"
|
56
|
+
logger.info(f"ACPI event: {ev}")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: adjustor
|
3
|
-
Version: 3.5.
|
3
|
+
Version: 3.5.3
|
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
|
@@ -20,6 +20,11 @@ src/adjustor/core/alib.py
|
|
20
20
|
src/adjustor/core/const.py
|
21
21
|
src/adjustor/core/lenovo.py
|
22
22
|
src/adjustor/core/platform.py
|
23
|
+
src/adjustor/core/fan/__init__.py
|
24
|
+
src/adjustor/core/fan/__main__.py
|
25
|
+
src/adjustor/core/fan/alg.py
|
26
|
+
src/adjustor/core/fan/core.py
|
27
|
+
src/adjustor/core/fan/utils.py
|
23
28
|
src/adjustor/drivers/__init__.py
|
24
29
|
src/adjustor/drivers/amd/__init__.py
|
25
30
|
src/adjustor/drivers/amd/power-profiles-daemon.dbus.xml.in
|
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.5.1 → adjustor-3.5.3}/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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{adjustor-3.5.1 → adjustor-3.5.3}/usr/share/dbus-1/system.d/hhd-net.hadess.PowerProfiles.conf
RENAMED
File without changes
|