-
Notifications
You must be signed in to change notification settings - Fork 194
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a plugin for amdgpu tuning of the
panel_power_savings
attribute
This attribute accepts a range from 0 through 4 where larger values will also have larger panel power savings. Using this has a trade off for color accuracy, and it is only applied when the system is currently operating on battery. The plugin uses upower to get a signal when battery changes so that it will react instantly. Intentionally the plugin will check what values are already programmed to the sysfs file to avoid unnecessary writes. Writing the sysfs file will cause a modeset which isn't necessary if writing the same value twice. The default values are applied to the profiles that are used in power-profiles-daemon compatbility. They also match the values used in that software. Signed-off-by: Mario Limonciello <[email protected]>
- Loading branch information
Showing
6 changed files
with
135 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,3 +29,6 @@ radeon_powersave=dpm-balanced, auto | |
|
||
[scsi_host] | ||
alpm=medium_power | ||
|
||
[amdgpu] | ||
panel_power_savings=3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,3 +41,6 @@ kernel.nmi_watchdog=0 | |
|
||
[script] | ||
script=${i:PROFILE_DIR}/script.sh | ||
|
||
[amdgpu] | ||
panel_power_savings=4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
from . import base | ||
from .decorators import * | ||
import errno | ||
import tuned.logs | ||
import dbus | ||
from tuned.utils.commands import commands | ||
|
||
log = tuned.logs.get() | ||
cmd = commands() | ||
|
||
UPOWER_DBUS_NAME = "org.freedesktop.UPower" | ||
UPOWER_DBUS_PATH = "/org/freedesktop/UPower" | ||
UPOWER_DBUS_INTERFACE = "org.freedesktop.UPower" | ||
|
||
|
||
class AmdgpuPlugin(base.Plugin): | ||
""" | ||
`amdgpu`:: | ||
Configures panel power savings on laptops with amdgpu driven eDP panels. | ||
This accepts a value range from 0 to 4, where 4 is the highest power savings | ||
but will trade off color accuracy. | ||
Settings will only be applied when the system is on battery. | ||
""" | ||
|
||
def __init__(self, *args, **kwargs): | ||
super(AmdgpuPlugin, self).__init__(*args, **kwargs) | ||
self.proxy = None | ||
|
||
def upower_changed(self, interface, changed, invalidated): | ||
properties = dbus.Interface(self.proxy, "org.freedesktop.DBus.Properties") | ||
self._on_battery = bool(properties.Get(UPOWER_DBUS_INTERFACE, "OnBattery")) | ||
log.debug(f"🔋: {self._on_battery}, 🎯: {self.target_value}") | ||
for device in self._assigned_devices: | ||
self.apply_target(device) | ||
|
||
def setup_battery_signaling(self): | ||
bus = dbus.SystemBus() | ||
self.proxy = bus.get_object(UPOWER_DBUS_NAME, UPOWER_DBUS_PATH) | ||
self.proxy.connect_to_signal("PropertiesChanged", self.upower_changed) | ||
self.upower_changed(None, None, None) | ||
|
||
def _init_devices(self): | ||
self._assigned_devices = set() | ||
self._free_devices = set() | ||
|
||
for device in ( | ||
self._hardware_inventory.get_devices("drm") | ||
.match_sys_name("card*eDP*") | ||
.match_sys_name("amdgpu") | ||
.match_sys_name("panel_power_savings") | ||
): | ||
self._free_devices.add(device.sys_name) | ||
self._devices_supported = len(self._free_devices) > 0 | ||
|
||
def _get_device_objects(self, devices): | ||
return [self._hardware_inventory.get_device("drm", x) for x in devices] | ||
|
||
@classmethod | ||
def _get_config_options(cls): | ||
return {"panel_power_savings": None} | ||
|
||
def _instance_init(self, instance): | ||
instance._has_static_tuning = True | ||
instance._has_dynamic_tuning = False | ||
|
||
def _amdgpu_files(self, device): | ||
return { | ||
"panel_power_savings": f"/sys/class/drm/{device}/amdgpu/panel_power_savings" | ||
} | ||
|
||
def _instance_cleanup(self, instance): | ||
pass | ||
|
||
def apply_target(self, device, sim=False): | ||
"""Apply the target value to the panel_power_savings file if it doesn't already have it""" | ||
|
||
# if we don't have a proxy, we can't tell if we're on battery | ||
if not self.proxy: | ||
self.setup_battery_signaling() | ||
return None | ||
|
||
# determine value to use (only apply if on battery, otherwise set to 0) | ||
if self._on_battery: | ||
target = self.target_value | ||
else: | ||
target = 0 | ||
|
||
# make sure the value is different (avoids unnecessary kernel modeset) | ||
current = int(self._get_panel_power_savings(device)) | ||
if current == target: | ||
log.info(f"panel_power_savings already {target} [🔋: {self._on_battery}]") | ||
return target | ||
|
||
# flush it out | ||
log.info(f"panel_power_savings -> {target} [🔋: {self._on_battery}]") | ||
if sim or cmd.write_to_file(self._amdgpu_files(device)["panel_power_savings"], target): | ||
return target | ||
return None | ||
|
||
@command_set("panel_power_savings", per_device=True) | ||
def _set_panel_power_savings(self, value, device, sim, remove): | ||
"""Set the panel_power_savings value""" | ||
try: | ||
value = int(value, 10) | ||
except ValueError: | ||
log.warn(f"Invalid value {value} for panel_power_savings") | ||
return None | ||
if value in range(0, 5): | ||
self.target_value = value | ||
return self.apply_target(device, sim) | ||
log.warn(f"Invalid value {value} for panel_power_savings") | ||
return None | ||
|
||
@command_get("panel_power_savings") | ||
def _get_panel_power_savings(self, device, ignore_missing=False): | ||
"""Get the current panel_power_savings value""" | ||
fname = self._amdgpu_files(device)["panel_power_savings"] | ||
return cmd.read_file(fname, no_error=ignore_missing).strip() |