mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
feat: initial support for RDNA3 fan configuration (PMFW) (#239)
* wip * feat: basic PMFW options gui * bump amdgpu-sysfs * test * test 2 * test 3 * revert test * fix: set pmfw settings last * fix: always set pmfw values * feat: reset pmfw button * fix: reset button placement * chore: cleanup * chore: bump version * feat: clean up pmfw settings on exit * fix
This commit is contained in:
parent
ce1cd56444
commit
2faea931d2
17
Cargo.lock
generated
17
Cargo.lock
generated
@ -41,9 +41,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "amdgpu-sysfs"
|
||||
version = "0.12.8"
|
||||
version = "0.12.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "165e7e777966011248a311f2c2e96ec1242935da4b829ac2c9a615ae936c0640"
|
||||
checksum = "b3a224a37eff351e9942de53949ad8822b0ef0e628a3fb41d3b78fd06f39ecfc"
|
||||
dependencies = [
|
||||
"enum_dispatch",
|
||||
"serde",
|
||||
@ -1270,7 +1270,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lact"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lact-cli",
|
||||
@ -1281,7 +1281,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lact-cli"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lact-client",
|
||||
@ -1290,7 +1290,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lact-client"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lact-schema",
|
||||
@ -1302,7 +1302,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lact-daemon"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1327,7 +1327,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lact-gui"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gtk4",
|
||||
@ -1341,13 +1341,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lact-schema"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"amdgpu-sysfs",
|
||||
"clap",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lact-cli"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lact-client"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
@ -10,8 +10,8 @@ use schema::{
|
||||
power_profile_mode::PowerProfileModesTable, PerformanceLevel, PowerLevelKind,
|
||||
},
|
||||
request::{ConfirmCommand, SetClocksCommand},
|
||||
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode, FanCurveMap, PowerStates,
|
||||
Request, Response, SystemInfo,
|
||||
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode, FanCurveMap, PmfwOptions,
|
||||
PowerStates, Request, Response, SystemInfo,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
@ -111,6 +111,7 @@ impl DaemonClient {
|
||||
mode: Option<FanControlMode>,
|
||||
static_speed: Option<f64>,
|
||||
curve: Option<FanCurveMap>,
|
||||
pmfw: PmfwOptions,
|
||||
) -> anyhow::Result<u64> {
|
||||
self.make_request(Request::SetFanControl {
|
||||
id,
|
||||
@ -118,6 +119,7 @@ impl DaemonClient {
|
||||
mode,
|
||||
static_speed,
|
||||
curve,
|
||||
pmfw,
|
||||
})?
|
||||
.inner()
|
||||
}
|
||||
@ -138,6 +140,7 @@ impl DaemonClient {
|
||||
PowerProfileModesTable
|
||||
);
|
||||
request_with_id!(get_power_states, GetPowerStates, PowerStates);
|
||||
request_with_id!(reset_pmfw, ResetPmfw, u64);
|
||||
|
||||
pub fn set_performance_level(
|
||||
&self,
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lact-daemon"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
|
@ -4,7 +4,7 @@ use lact_schema::{
|
||||
amdgpu_sysfs::gpu_handle::{PerformanceLevel, PowerLevelKind},
|
||||
default_fan_curve,
|
||||
request::SetClocksCommand,
|
||||
FanControlMode,
|
||||
FanControlMode, PmfwOptions,
|
||||
};
|
||||
use nix::unistd::getuid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -56,6 +56,8 @@ impl Default for Daemon {
|
||||
pub struct Gpu {
|
||||
pub fan_control_enabled: bool,
|
||||
pub fan_control_settings: Option<FanControlSettings>,
|
||||
#[serde(default)]
|
||||
pub pmfw_options: PmfwOptions,
|
||||
pub power_cap: Option<f64>,
|
||||
pub performance_level: Option<PerformanceLevel>,
|
||||
#[serde(default, flatten)]
|
||||
@ -179,10 +181,9 @@ fn default_apply_settings_timer() -> u64 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lact_schema::{FanControlMode, PmfwOptions};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use lact_schema::FanControlMode;
|
||||
|
||||
use super::{ClocksConfiguration, Config, Daemon, FanControlSettings, Gpu};
|
||||
use crate::server::gpu_controller::fan_control::FanCurve;
|
||||
|
||||
@ -217,6 +218,7 @@ mod tests {
|
||||
let mut gpu = Gpu {
|
||||
fan_control_enabled: false,
|
||||
fan_control_settings: None,
|
||||
pmfw_options: PmfwOptions::default(),
|
||||
power_cap: None,
|
||||
performance_level: None,
|
||||
clocks_configuration: ClocksConfiguration::default(),
|
||||
|
@ -18,7 +18,7 @@ use lact_schema::{
|
||||
sysfs::SysFS,
|
||||
},
|
||||
ClocksInfo, ClockspeedStats, DeviceInfo, DeviceStats, DrmInfo, FanStats, GpuPciInfo, LinkInfo,
|
||||
PciInfo, PowerState, PowerStates, PowerStats, VoltageStats, VramStats,
|
||||
PciInfo, PmfwInfo, PowerState, PowerStates, PowerStats, VoltageStats, VramStats,
|
||||
};
|
||||
use pciid_parser::Database;
|
||||
use std::{
|
||||
@ -262,6 +262,12 @@ impl GpuController {
|
||||
speed_current: self.hw_mon_and_then(HwMon::get_fan_current),
|
||||
speed_max: self.hw_mon_and_then(HwMon::get_fan_max),
|
||||
speed_min: self.hw_mon_and_then(HwMon::get_fan_min),
|
||||
pmfw_info: PmfwInfo {
|
||||
acoustic_limit: self.handle.get_fan_acoustic_limit().ok(),
|
||||
acoustic_target: self.handle.get_fan_acoustic_target().ok(),
|
||||
target_temp: self.handle.get_fan_target_temperature().ok(),
|
||||
minimum_pwm: self.handle.get_fan_minimum_pwm().ok(),
|
||||
},
|
||||
},
|
||||
clockspeed: ClockspeedStats {
|
||||
gpu_clockspeed: self.hw_mon_and_then(HwMon::get_gpu_clockspeed),
|
||||
@ -468,6 +474,30 @@ impl GpuController {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn reset_pmfw_settings(&self) {
|
||||
let handle = &self.handle;
|
||||
if self.handle.get_fan_target_temperature().is_ok() {
|
||||
if let Err(err) = handle.reset_fan_target_temperature() {
|
||||
warn!("Could not reset target temperature: {err:#}");
|
||||
}
|
||||
}
|
||||
if self.handle.get_fan_acoustic_target().is_ok() {
|
||||
if let Err(err) = handle.reset_fan_acoustic_target() {
|
||||
warn!("Could not reset acoustic target: {err:#}");
|
||||
}
|
||||
}
|
||||
if self.handle.get_fan_acoustic_limit().is_ok() {
|
||||
if let Err(err) = handle.reset_fan_acoustic_limit() {
|
||||
warn!("Could not reset acoustic limit: {err:#}");
|
||||
}
|
||||
}
|
||||
if self.handle.get_fan_minimum_pwm().is_ok() {
|
||||
if let Err(err) = handle.reset_fan_minimum_pwm() {
|
||||
warn!("Could not reset minimum pwm: {err:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn apply_config(&self, config: &config::Gpu) -> anyhow::Result<()> {
|
||||
if config.fan_control_enabled {
|
||||
if let Some(ref settings) = config.fan_control_settings {
|
||||
@ -598,6 +628,30 @@ impl GpuController {
|
||||
.with_context(|| format!("Could not set {kind:?} power states"))?;
|
||||
}
|
||||
|
||||
if !config.fan_control_enabled {
|
||||
let pmfw = &config.pmfw_options;
|
||||
if let Some(acoustic_limit) = pmfw.acoustic_limit {
|
||||
self.handle
|
||||
.set_fan_acoustic_limit(acoustic_limit)
|
||||
.context("Could not set acoustic limit")?;
|
||||
}
|
||||
if let Some(acoustic_target) = pmfw.acoustic_target {
|
||||
self.handle
|
||||
.set_fan_acoustic_target(acoustic_target)
|
||||
.context("Could not set acoustic target")?;
|
||||
}
|
||||
if let Some(target_temperature) = pmfw.target_temperature {
|
||||
self.handle
|
||||
.set_fan_target_temperature(target_temperature)
|
||||
.context("Could not set target temperature")?;
|
||||
}
|
||||
if let Some(minimum_pwm) = pmfw.minimum_pwm {
|
||||
self.handle
|
||||
.set_fan_minimum_pwm(minimum_pwm)
|
||||
.context("Could not set minimum pwm")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ use lact_schema::{
|
||||
},
|
||||
default_fan_curve,
|
||||
request::{ConfirmCommand, SetClocksCommand},
|
||||
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode, FanCurveMap, PowerStates,
|
||||
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode, FanCurveMap, PmfwOptions,
|
||||
PowerStates,
|
||||
};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
@ -263,6 +264,7 @@ impl<'a> Handler {
|
||||
mode: Option<FanControlMode>,
|
||||
static_speed: Option<f64>,
|
||||
curve: Option<FanCurveMap>,
|
||||
pmfw: PmfwOptions,
|
||||
) -> anyhow::Result<u64> {
|
||||
let settings = {
|
||||
let mut config_guard = self
|
||||
@ -323,6 +325,17 @@ impl<'a> Handler {
|
||||
if let Some(settings) = settings {
|
||||
config.fan_control_settings = Some(settings);
|
||||
}
|
||||
config.pmfw_options = pmfw;
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn reset_pmfw(&self, id: &str) -> anyhow::Result<u64> {
|
||||
info!("Resetting PMFW settings");
|
||||
self.controller_by_id(id)?.reset_pmfw_settings();
|
||||
|
||||
self.edit_gpu_config(id.to_owned(), |config| {
|
||||
config.pmfw_options = PmfwOptions::default();
|
||||
})
|
||||
.await
|
||||
}
|
||||
@ -510,6 +523,8 @@ impl<'a> Handler {
|
||||
}
|
||||
}
|
||||
|
||||
controller.reset_pmfw_settings();
|
||||
|
||||
if let Err(err) = controller.apply_config(&config::Gpu::default()).await {
|
||||
error!("Could not reset settings for controller {id}: {err:#}");
|
||||
}
|
||||
|
@ -92,11 +92,13 @@ async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyho
|
||||
mode,
|
||||
static_speed,
|
||||
curve,
|
||||
pmfw,
|
||||
} => ok_response(
|
||||
handler
|
||||
.set_fan_control(id, enabled, mode, static_speed, curve)
|
||||
.set_fan_control(id, enabled, mode, static_speed, curve, pmfw)
|
||||
.await?,
|
||||
),
|
||||
Request::ResetPmfw { id } => ok_response(handler.reset_pmfw(id).await?),
|
||||
Request::SetPowerCap { id, cap } => ok_response(handler.set_power_cap(id, cap).await?),
|
||||
Request::SetPerformanceLevel {
|
||||
id,
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lact-gui"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
authors = ["Ilya Zlobintsev <ilya.zl@protonmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -125,6 +125,23 @@ impl App {
|
||||
}
|
||||
}));
|
||||
|
||||
app.root_stack.thermals_page.connect_reset_pmfw(clone!(@strong app, @strong current_gpu_id => move || {
|
||||
debug!("Resetting PMFW settings");
|
||||
let gpu_id = current_gpu_id.borrow().clone();
|
||||
|
||||
match app.daemon_client.reset_pmfw(&gpu_id)
|
||||
.and_then(|buffer| buffer.inner())
|
||||
.and_then(|_| app.daemon_client.confirm_pending_config(ConfirmCommand::Confirm))
|
||||
{
|
||||
Ok(()) => {
|
||||
app.set_initial(&gpu_id);
|
||||
}
|
||||
Err(err) => {
|
||||
show_error(&app.window, err);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
app.apply_revealer.connect_apply_button_clicked(
|
||||
clone!(@strong app, @strong current_gpu_id => move || {
|
||||
glib::idle_add_local_once(clone!(@strong app, @strong current_gpu_id => move || {
|
||||
@ -389,6 +406,7 @@ impl App {
|
||||
thermals_settings.mode,
|
||||
thermals_settings.static_speed,
|
||||
thermals_settings.curve,
|
||||
thermals_settings.pmfw,
|
||||
)
|
||||
.context("Could not set fan control")?;
|
||||
self.daemon_client
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod info_page;
|
||||
mod oc_adjustment;
|
||||
mod oc_page;
|
||||
mod software_page;
|
||||
mod thermals_page;
|
||||
|
@ -2,7 +2,7 @@ mod imp;
|
||||
|
||||
use glib::Object;
|
||||
use gtk::{
|
||||
glib::{self},
|
||||
glib::{self, ObjectExt},
|
||||
subclass::prelude::*,
|
||||
traits::AdjustmentExt,
|
||||
};
|
||||
@ -57,9 +57,19 @@ impl OcAdjustment {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_nonzero_value(&self) -> Option<f64> {
|
||||
let value = self.value();
|
||||
if value == 0.0 {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_initial_value(&self, value: f64) {
|
||||
let inner = self.imp();
|
||||
inner.obj().set_value(value);
|
||||
inner.obj().emit_by_name::<()>("value_changed", &[]);
|
||||
inner.changed.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
mod clocks_frame;
|
||||
mod gpu_stats_section;
|
||||
mod oc_adjustment;
|
||||
mod performance_frame;
|
||||
mod power_cap_section;
|
||||
// mod power_cap_frame;
|
||||
|
@ -29,7 +29,7 @@ impl Default for PowerCapSection {
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use crate::app::{page_section::PageSection, root_stack::oc_page::oc_adjustment::OcAdjustment};
|
||||
use crate::app::{page_section::PageSection, root_stack::oc_adjustment::OcAdjustment};
|
||||
use gtk::{
|
||||
glib::{self, clone, subclass::InitializingObject, Properties, StaticTypeExt},
|
||||
prelude::{ButtonExt, ObjectExt},
|
||||
|
@ -1,15 +1,16 @@
|
||||
mod fan_curve_frame;
|
||||
mod pmfw_frame;
|
||||
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_client::schema::{default_fan_curve, DeviceStats, FanControlMode, FanCurveMap};
|
||||
|
||||
use crate::app::page_section::PageSection;
|
||||
|
||||
use self::fan_curve_frame::FanCurveFrame;
|
||||
use lact_client::schema::{
|
||||
default_fan_curve, DeviceStats, FanControlMode, FanCurveMap, PmfwOptions,
|
||||
};
|
||||
|
||||
use self::{fan_curve_frame::FanCurveFrame, pmfw_frame::PmfwFrame};
|
||||
use super::{label_row, values_grid};
|
||||
use crate::app::page_section::PageSection;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ThermalsSettings {
|
||||
@ -17,6 +18,7 @@ pub struct ThermalsSettings {
|
||||
pub mode: Option<FanControlMode>,
|
||||
pub static_speed: Option<f64>,
|
||||
pub curve: Option<FanCurveMap>,
|
||||
pub pmfw: PmfwOptions,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -24,6 +26,7 @@ pub struct ThermalsPage {
|
||||
pub container: Box,
|
||||
temperatures_label: Label,
|
||||
fan_speed_label: Label,
|
||||
pmfw_frame: PmfwFrame,
|
||||
fan_static_speed_adjustment: Adjustment,
|
||||
fan_curve_frame: FanCurveFrame,
|
||||
fan_control_mode_stack: Stack,
|
||||
@ -58,6 +61,8 @@ impl ThermalsPage {
|
||||
.build();
|
||||
let fan_static_speed_adjustment = static_speed_adj(&fan_static_speed_frame);
|
||||
|
||||
let pmfw_frame = PmfwFrame::new();
|
||||
|
||||
let fan_control_section = PageSection::new("Fan control");
|
||||
|
||||
let fan_control_mode_stack = Stack::builder().build();
|
||||
@ -67,14 +72,8 @@ impl ThermalsPage {
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
fan_control_mode_stack.add_titled(
|
||||
&Box::new(Orientation::Vertical, 15),
|
||||
Some("automatic"),
|
||||
"Automatic",
|
||||
);
|
||||
|
||||
fan_control_mode_stack.add_titled(&pmfw_frame.container, Some("automatic"), "Automatic");
|
||||
fan_control_mode_stack.add_titled(&fan_curve_frame.container, Some("curve"), "Curve");
|
||||
|
||||
fan_control_mode_stack.add_titled(&fan_static_speed_frame, Some("static"), "Static");
|
||||
|
||||
fan_control_section.append(&fan_control_mode_stack_switcher);
|
||||
@ -96,6 +95,7 @@ impl ThermalsPage {
|
||||
fan_curve_frame,
|
||||
fan_control_mode_stack,
|
||||
fan_control_mode_stack_switcher,
|
||||
pmfw_frame,
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,6 +155,8 @@ impl ThermalsPage {
|
||||
if !stats.fan.control_enabled && self.fan_curve_frame.get_curve().is_empty() {
|
||||
self.fan_curve_frame.set_curve(&default_fan_curve());
|
||||
}
|
||||
|
||||
self.pmfw_frame.set_info(&stats.fan.pmfw_info);
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,6 +171,8 @@ impl ThermalsPage {
|
||||
f();
|
||||
}));
|
||||
|
||||
self.pmfw_frame.connect_settings_changed(f.clone());
|
||||
|
||||
self.fan_curve_frame.connect_adjusted(move || {
|
||||
f();
|
||||
});
|
||||
@ -191,16 +195,23 @@ impl ThermalsPage {
|
||||
let curve = self.fan_curve_frame.get_curve();
|
||||
let curve = if curve.is_empty() { None } else { Some(curve) };
|
||||
|
||||
let pmfw = self.pmfw_frame.get_pmfw_options();
|
||||
|
||||
Some(ThermalsSettings {
|
||||
manual_fan_control,
|
||||
mode,
|
||||
static_speed,
|
||||
curve,
|
||||
pmfw,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect_reset_pmfw<F: Fn() + 'static + Clone>(&self, f: F) {
|
||||
self.pmfw_frame.connect_reset(f);
|
||||
}
|
||||
}
|
||||
|
||||
fn static_speed_adj(parent_box: &Box) -> Adjustment {
|
||||
|
179
lact-gui/src/app/root_stack/thermals_page/pmfw_frame.rs
Normal file
179
lact-gui/src/app/root_stack/thermals_page/pmfw_frame.rs
Normal file
@ -0,0 +1,179 @@
|
||||
use crate::app::root_stack::oc_adjustment::OcAdjustment;
|
||||
use gtk::{
|
||||
glib::clone,
|
||||
prelude::{AdjustmentExt, ButtonExt, GridExt, WidgetExt},
|
||||
Align, Button, Grid, Label, MenuButton, Orientation, Popover, Scale, SpinButton,
|
||||
};
|
||||
use lact_client::schema::{amdgpu_sysfs::gpu_handle::fan_control::FanInfo, PmfwInfo, PmfwOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PmfwFrame {
|
||||
pub container: Grid,
|
||||
target_temperature: OcAdjustment,
|
||||
acoustic_limit: OcAdjustment,
|
||||
acoustic_target: OcAdjustment,
|
||||
minimum_pwm: OcAdjustment,
|
||||
reset_button: Button,
|
||||
}
|
||||
|
||||
impl PmfwFrame {
|
||||
pub fn new() -> Self {
|
||||
let grid = Grid::builder()
|
||||
.orientation(Orientation::Vertical)
|
||||
.row_spacing(5)
|
||||
.margin_top(10)
|
||||
.margin_bottom(10)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.build();
|
||||
|
||||
let target_temperature = adjustment(&grid, "Target temperature (°C)", 0);
|
||||
let acoustic_limit = adjustment(&grid, "Acoustic limit (RPM)", 1);
|
||||
let acoustic_target = adjustment(&grid, "Acoustic target (RPM)", 2);
|
||||
let minimum_pwm = adjustment(&grid, "Minimum fan speed (%)", 3);
|
||||
|
||||
let reset_button = Button::builder()
|
||||
.label("Reset")
|
||||
.halign(Align::Fill)
|
||||
.margin_top(5)
|
||||
.margin_bottom(5)
|
||||
.tooltip_text("Warning: this resets the fan firmware settings!")
|
||||
.css_classes(["destructive-action"])
|
||||
.visible(false)
|
||||
.build();
|
||||
grid.attach(&reset_button, 5, 4, 1, 1);
|
||||
|
||||
Self {
|
||||
container: grid,
|
||||
target_temperature,
|
||||
acoustic_limit,
|
||||
acoustic_target,
|
||||
minimum_pwm,
|
||||
reset_button,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_info(&self, info: &PmfwInfo) {
|
||||
set_fan_info(&self.acoustic_limit, info.acoustic_limit);
|
||||
set_fan_info(&self.acoustic_target, info.acoustic_target);
|
||||
set_fan_info(&self.minimum_pwm, info.minimum_pwm);
|
||||
set_fan_info(&self.target_temperature, info.target_temp);
|
||||
|
||||
let settings_available = *info != PmfwInfo::default();
|
||||
self.reset_button.set_visible(settings_available);
|
||||
}
|
||||
|
||||
pub fn connect_settings_changed<F: Fn() + 'static + Clone>(&self, f: F) {
|
||||
self.acoustic_limit
|
||||
.connect_value_changed(clone!(@strong f => move |_| {
|
||||
f();
|
||||
}));
|
||||
self.acoustic_target
|
||||
.connect_value_changed(clone!(@strong f => move |_| {
|
||||
f();
|
||||
}));
|
||||
self.minimum_pwm
|
||||
.connect_value_changed(clone!(@strong f => move |_| {
|
||||
f();
|
||||
}));
|
||||
self.target_temperature
|
||||
.connect_value_changed(clone!(@strong f => move |_| {
|
||||
f();
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn connect_reset<F: Fn() + 'static + Clone>(&self, f: F) {
|
||||
self.reset_button.connect_clicked(move |_| {
|
||||
f();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_pmfw_options(&self) -> PmfwOptions {
|
||||
PmfwOptions {
|
||||
acoustic_limit: self
|
||||
.acoustic_limit
|
||||
.get_nonzero_value()
|
||||
.map(|value| value as u32),
|
||||
acoustic_target: self
|
||||
.acoustic_target
|
||||
.get_nonzero_value()
|
||||
.map(|value| value as u32),
|
||||
minimum_pwm: self
|
||||
.minimum_pwm
|
||||
.get_nonzero_value()
|
||||
.map(|value| value as u32),
|
||||
target_temperature: self
|
||||
.target_temperature
|
||||
.get_nonzero_value()
|
||||
.map(|value| value as u32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fan_info(adjustment: &OcAdjustment, info: Option<FanInfo>) {
|
||||
match info {
|
||||
Some(info) => {
|
||||
if let Some((min, max)) = info.allowed_range {
|
||||
adjustment.set_lower(min as f64);
|
||||
adjustment.set_upper(max as f64);
|
||||
} else {
|
||||
adjustment.set_lower(0.0);
|
||||
adjustment.set_upper(info.current as f64);
|
||||
}
|
||||
|
||||
adjustment.set_initial_value(info.current as f64);
|
||||
}
|
||||
None => {
|
||||
adjustment.set_upper(0.0);
|
||||
adjustment.set_initial_value(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn adjustment(parent_grid: &Grid, label: &str, row: i32) -> OcAdjustment {
|
||||
let label = Label::builder().label(label).halign(Align::Start).build();
|
||||
|
||||
let adjustment = OcAdjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 0.0);
|
||||
|
||||
let scale = Scale::builder()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.adjustment(&adjustment)
|
||||
.hexpand(true)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.build();
|
||||
|
||||
let value_selector = SpinButton::new(Some(&adjustment), 1.0, 0);
|
||||
let value_label = Label::new(None);
|
||||
|
||||
let popover = Popover::builder().child(&value_selector).build();
|
||||
let value_button = MenuButton::builder()
|
||||
.popover(&popover)
|
||||
.child(&value_label)
|
||||
.build();
|
||||
|
||||
adjustment.connect_value_changed(
|
||||
clone!(@strong value_label, @strong label, @strong scale, @strong value_button => move |adjustment| {
|
||||
let value = adjustment.value();
|
||||
value_label.set_text(&format!("{}", value as u32));
|
||||
|
||||
if adjustment.upper() == 0.0 {
|
||||
label.hide();
|
||||
value_label.hide();
|
||||
scale.hide();
|
||||
value_button.hide();
|
||||
} else {
|
||||
label.show();
|
||||
value_label.show();
|
||||
scale.show();
|
||||
value_button.show();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
parent_grid.attach(&label, 0, row, 1, 1);
|
||||
parent_grid.attach(&scale, 1, row, 4, 1);
|
||||
parent_grid.attach(&value_button, 5, row, 1, 1);
|
||||
|
||||
adjustment
|
||||
}
|
@ -1,16 +1,19 @@
|
||||
[package]
|
||||
name = "lact-schema"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
args = ["clap"]
|
||||
|
||||
[dependencies]
|
||||
amdgpu-sysfs = { version = "0.12.7", features = ["serde"] }
|
||||
amdgpu-sysfs = { version = "0.12.9", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
indexmap = { version = "*", features = ["serde"] }
|
||||
clap = { version = "4.4.11", features = ["derive"], optional = true }
|
||||
serde_with = { version = "3.4.0", default-features = false, features = [
|
||||
"macros",
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0"
|
||||
|
@ -12,6 +12,7 @@ pub use response::Response;
|
||||
|
||||
use amdgpu_sysfs::{
|
||||
gpu_handle::{
|
||||
fan_control::FanInfo,
|
||||
overdrive::{ClocksTable, ClocksTableGen},
|
||||
PerformanceLevel,
|
||||
},
|
||||
@ -19,6 +20,7 @@ use amdgpu_sysfs::{
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{BTreeMap, HashMap},
|
||||
@ -195,6 +197,18 @@ pub struct FanStats {
|
||||
pub speed_current: Option<u32>,
|
||||
pub speed_max: Option<u32>,
|
||||
pub speed_min: Option<u32>,
|
||||
// RDNA3+ params
|
||||
#[serde(default)]
|
||||
pub pmfw_info: PmfwInfo,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct PmfwInfo {
|
||||
pub acoustic_limit: Option<FanInfo>,
|
||||
pub acoustic_target: Option<FanInfo>,
|
||||
pub target_temp: Option<FanInfo>,
|
||||
pub minimum_pwm: Option<FanInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||
@ -248,3 +262,12 @@ pub enum InitramfsType {
|
||||
Debian,
|
||||
Mkinitcpio,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct PmfwOptions {
|
||||
pub acoustic_limit: Option<u32>,
|
||||
pub acoustic_target: Option<u32>,
|
||||
pub minimum_pwm: Option<u32>,
|
||||
pub target_temperature: Option<u32>,
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{FanControlMode, FanCurveMap};
|
||||
use crate::{FanControlMode, FanCurveMap, PmfwOptions};
|
||||
use amdgpu_sysfs::gpu_handle::{PerformanceLevel, PowerLevelKind};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -26,6 +26,11 @@ pub enum Request<'a> {
|
||||
mode: Option<FanControlMode>,
|
||||
static_speed: Option<f64>,
|
||||
curve: Option<FanCurveMap>,
|
||||
#[serde(default)]
|
||||
pmfw: PmfwOptions,
|
||||
},
|
||||
ResetPmfw {
|
||||
id: &'a str,
|
||||
},
|
||||
SetPowerCap {
|
||||
id: &'a str,
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lact"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
|
@ -3,7 +3,7 @@ metadata:
|
||||
description: AMDGPU control utility
|
||||
arch: x86_64
|
||||
license: MIT
|
||||
version: 0.5.1
|
||||
version: 0.5.2
|
||||
maintainer: ilya-zlobintsev
|
||||
url: https://github.com/ilya-zlobintsev/lact
|
||||
source:
|
||||
|
@ -3,7 +3,7 @@ metadata:
|
||||
description: AMDGPU control utility
|
||||
arch: x86_64
|
||||
license: MIT
|
||||
version: 0.5.1
|
||||
version: 0.5.2
|
||||
maintainer: ilya-zlobintsev
|
||||
url: https://github.com/ilya-zlobintsev/lact
|
||||
source:
|
||||
|
@ -3,7 +3,7 @@ metadata:
|
||||
description: AMDGPU control utility
|
||||
arch: x86_64
|
||||
license: MIT
|
||||
version: 0.5.1
|
||||
version: 0.5.2
|
||||
maintainer: ilya-zlobintsev
|
||||
url: https://github.com/ilya-zlobintsev/lact
|
||||
source:
|
||||
|
Loading…
Reference in New Issue
Block a user