Combine SD advance mode back into regular SD driver, implement custom clock file support
This commit is contained in:
parent
5fe4e9d15d
commit
1dc0acca8e
15 changed files with 318 additions and 884 deletions
89
backend/limits_core/Cargo.lock
generated
Normal file
89
backend/limits_core/Cargo.lock
generated
Normal file
|
@ -0,0 +1,89 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "limits_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
|
@ -14,6 +14,21 @@ impl Default for Base {
|
|||
fn default() -> Self {
|
||||
Base {
|
||||
configs: vec![
|
||||
super::Config {
|
||||
name: "Steam Deck Custom".to_owned(),
|
||||
conditions: super::Conditions {
|
||||
dmi: None,
|
||||
cpuinfo: Some("model name\t: AMD Custom APU 0405\n".to_owned()),
|
||||
os: None,
|
||||
command: None,
|
||||
file_exists: Some("./pt_oc.json".into()),
|
||||
},
|
||||
limits: vec![
|
||||
super::Limits::Cpu(super::CpuLimit::SteamDeckAdvance),
|
||||
super::Limits::Gpu(super::GpuLimit::SteamDeckAdvance),
|
||||
super::Limits::Battery(super::BatteryLimit::SteamDeckAdvance),
|
||||
]
|
||||
},
|
||||
super::Config {
|
||||
name: "Steam Deck".to_owned(),
|
||||
conditions: super::Conditions {
|
||||
|
@ -21,6 +36,7 @@ impl Default for Base {
|
|||
cpuinfo: Some("model name\t: AMD Custom APU 0405\n".to_owned()),
|
||||
os: None,
|
||||
command: None,
|
||||
file_exists: None,
|
||||
},
|
||||
limits: vec![
|
||||
super::Limits::Cpu(super::CpuLimit::SteamDeck),
|
||||
|
@ -35,6 +51,7 @@ impl Default for Base {
|
|||
cpuinfo: None,
|
||||
os: None,
|
||||
command: None,
|
||||
file_exists: None,
|
||||
},
|
||||
limits: vec![
|
||||
super::Limits::Cpu(super::CpuLimit::Unknown),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Conditions under which a config applies
|
||||
/// Conditions under which a config applies (ANDed together)
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Conditions {
|
||||
/// Regex pattern for dmidecode output
|
||||
|
@ -11,6 +11,8 @@ pub struct Conditions {
|
|||
pub os: Option<String>,
|
||||
/// Custom command to run, where an exit code of 0 means a successful match
|
||||
pub command: Option<String>,
|
||||
/// Check if file exists
|
||||
pub file_exists: Option<String>,
|
||||
}
|
||||
|
||||
impl Conditions {
|
||||
|
|
|
@ -1,5 +1,26 @@
|
|||
{
|
||||
"configs": [
|
||||
{
|
||||
"name": "Steam Deck Custom",
|
||||
"conditions": {
|
||||
"cpuinfo": "model name\t: AMD Custom APU 0405\n",
|
||||
"file_exists": "./pt_oc.json"
|
||||
},
|
||||
"limits": [
|
||||
{
|
||||
"limits": "Cpu",
|
||||
"target": "SteamDeck"
|
||||
},
|
||||
{
|
||||
"limits": "Gpu",
|
||||
"target": "SteamDeck"
|
||||
},
|
||||
{
|
||||
"limits": "Battery",
|
||||
"target": "SteamDeck"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Steam Deck",
|
||||
"conditions": {
|
||||
|
|
|
@ -84,6 +84,10 @@ pub fn auto_detect0(settings_opt: Option<SettingsJson>, json_path: std::path::Pa
|
|||
Err(e) => log::warn!("Ignoring bash limits error: {}", e),
|
||||
}
|
||||
}
|
||||
if let Some(file_exists) = &conditions.file_exists {
|
||||
let exists = std::path::Path::new(file_exists).exists();
|
||||
matches &= exists;
|
||||
}
|
||||
}
|
||||
if matches {
|
||||
if let Some(settings) = &settings_opt {
|
||||
|
@ -92,7 +96,7 @@ pub fn auto_detect0(settings_opt: Option<SettingsJson>, json_path: std::path::Pa
|
|||
Limits::Cpu(cpus) => {
|
||||
let cpu_driver: Box<dyn TCpus> = match cpus {
|
||||
CpuLimit::SteamDeck => Box::new(crate::settings::steam_deck::Cpus::from_json(settings.cpus.clone(), settings.version)),
|
||||
CpuLimit::SteamDeckAdvance => Box::new(crate::settings::steam_deck_adv::Cpus::from_json(settings.cpus.clone(), settings.version)),
|
||||
CpuLimit::SteamDeckAdvance => Box::new(crate::settings::steam_deck::Cpus::from_json(settings.cpus.clone(), settings.version)),
|
||||
CpuLimit::Generic(x) => Box::new(crate::settings::generic::Cpus::from_json_and_limits(settings.cpus.clone(), settings.version, x)),
|
||||
CpuLimit::Unknown => Box::new(crate::settings::unknown::Cpus::from_json(settings.cpus.clone(), settings.version)),
|
||||
};
|
||||
|
@ -101,7 +105,7 @@ pub fn auto_detect0(settings_opt: Option<SettingsJson>, json_path: std::path::Pa
|
|||
Limits::Gpu(gpu) => {
|
||||
let driver: Box<dyn TGpu> = match gpu {
|
||||
GpuLimit::SteamDeck => Box::new(crate::settings::steam_deck::Gpu::from_json(settings.gpu.clone(), settings.version)),
|
||||
GpuLimit::SteamDeckAdvance => Box::new(crate::settings::steam_deck_adv::Gpu::from_json(settings.gpu.clone(), settings.version)),
|
||||
GpuLimit::SteamDeckAdvance => Box::new(crate::settings::steam_deck::Gpu::from_json(settings.gpu.clone(), settings.version)),
|
||||
GpuLimit::Generic(x) => Box::new(crate::settings::generic::Gpu::from_json_and_limits(settings.gpu.clone(), settings.version, x)),
|
||||
GpuLimit::Unknown => Box::new(crate::settings::unknown::Gpu::from_json(settings.gpu.clone(), settings.version)),
|
||||
};
|
||||
|
@ -124,7 +128,7 @@ pub fn auto_detect0(settings_opt: Option<SettingsJson>, json_path: std::path::Pa
|
|||
Limits::Cpu(cpus) => {
|
||||
let cpu_driver: Box<dyn TCpus> = match cpus {
|
||||
CpuLimit::SteamDeck => Box::new(crate::settings::steam_deck::Cpus::system_default()),
|
||||
CpuLimit::SteamDeckAdvance => Box::new(crate::settings::steam_deck_adv::Cpus::system_default()),
|
||||
CpuLimit::SteamDeckAdvance => Box::new(crate::settings::steam_deck::Cpus::system_default()),
|
||||
CpuLimit::Generic(x) => Box::new(crate::settings::generic::Cpus::from_limits(x)),
|
||||
CpuLimit::Unknown => Box::new(crate::settings::unknown::Cpus::system_default()),
|
||||
};
|
||||
|
@ -133,7 +137,7 @@ pub fn auto_detect0(settings_opt: Option<SettingsJson>, json_path: std::path::Pa
|
|||
Limits::Gpu(gpu) => {
|
||||
let driver: Box<dyn TGpu> = match gpu {
|
||||
GpuLimit::SteamDeck => Box::new(crate::settings::steam_deck::Gpu::system_default()),
|
||||
GpuLimit::SteamDeckAdvance => Box::new(crate::settings::steam_deck_adv::Gpu::system_default()),
|
||||
GpuLimit::SteamDeckAdvance => Box::new(crate::settings::steam_deck::Gpu::system_default()),
|
||||
GpuLimit::Generic(x) => Box::new(crate::settings::generic::Gpu::from_limits(x)),
|
||||
GpuLimit::Unknown => Box::new(crate::settings::unknown::Gpu::system_default()),
|
||||
};
|
||||
|
|
|
@ -40,6 +40,7 @@ impl Driver {
|
|||
gpu: Box::new(super::steam_deck::Gpu::from_json(settings.gpu, settings.version)),
|
||||
battery: Box::new(super::steam_deck::Battery::from_json(settings.battery, settings.version)),
|
||||
}),
|
||||
// There's nothing special about SteamDeckAdvance, it just appears different
|
||||
DriverJson::SteamDeckAdvance => Ok(Self {
|
||||
general: Box::new(General {
|
||||
persistent: settings.persistent,
|
||||
|
@ -47,8 +48,8 @@ impl Driver {
|
|||
name: settings.name,
|
||||
driver: DriverJson::SteamDeckAdvance,
|
||||
}),
|
||||
cpus: Box::new(super::steam_deck_adv::Cpus::from_json(settings.cpus, settings.version)),
|
||||
gpu: Box::new(super::steam_deck_adv::Gpu::from_json(settings.gpu, settings.version)),
|
||||
cpus: Box::new(super::steam_deck::Cpus::from_json(settings.cpus, settings.version)),
|
||||
gpu: Box::new(super::steam_deck::Gpu::from_json(settings.gpu, settings.version)),
|
||||
battery: Box::new(super::steam_deck::Battery::from_json(settings.battery, settings.version)),
|
||||
}),
|
||||
DriverJson::Generic => Ok(Self {
|
||||
|
|
|
@ -7,7 +7,6 @@ mod traits;
|
|||
|
||||
pub mod generic;
|
||||
pub mod steam_deck;
|
||||
pub mod steam_deck_adv;
|
||||
pub mod unknown;
|
||||
|
||||
pub use detect::{auto_detect0, auto_detect_provider, limits_worker::spawn as limits_worker_spawn};
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use crate::api::RangeLimit;
|
||||
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::settings::{OnResume, OnSet, SettingError};
|
||||
use crate::settings::TBattery;
|
||||
use crate::persist::BatteryJson;
|
||||
use super::util::ChargeMode;
|
||||
use super::oc_limits::{BatteryLimits, OverclockLimits};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Battery {
|
||||
pub charge_rate: Option<u64>,
|
||||
pub charge_mode: Option<ChargeMode>,
|
||||
limits: BatteryLimits,
|
||||
state: crate::state::steam_deck::Battery,
|
||||
}
|
||||
|
||||
|
@ -24,15 +26,18 @@ const BATTERY_CHARGE_DESIGN_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_
|
|||
impl Battery {
|
||||
#[inline]
|
||||
pub fn from_json(other: BatteryJson, version: u64) -> Self {
|
||||
let oc_limits = OverclockLimits::load_or_default().battery;
|
||||
match version {
|
||||
0 => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(),
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
},
|
||||
_ => Self {
|
||||
charge_rate: other.charge_rate,
|
||||
charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(),
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
},
|
||||
}
|
||||
|
@ -68,7 +73,7 @@ impl Battery {
|
|||
)?;
|
||||
} else if self.state.charge_rate_set {
|
||||
self.state.charge_rate_set = false;
|
||||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, Self::max().charge_rate.unwrap()).map_err(
|
||||
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, self.limits.charge_rate.max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Battery,
|
||||
|
@ -96,10 +101,8 @@ impl Battery {
|
|||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(charge_rate) = &mut self.charge_rate {
|
||||
*charge_rate = (*charge_rate).clamp(min.charge_rate.unwrap(), max.charge_rate.unwrap());
|
||||
*charge_rate = (*charge_rate).clamp(self.limits.charge_rate.min, self.limits.charge_rate.max);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,9 +184,11 @@ impl Battery {
|
|||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
let oc_limits = OverclockLimits::load_or_default().battery;
|
||||
Self {
|
||||
charge_rate: None,
|
||||
charge_mode: None,
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
|
@ -212,34 +217,12 @@ impl OnResume for Battery {
|
|||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Battery {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
charge_rate: Some(2500),
|
||||
charge_mode: None,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
charge_rate: Some(250),
|
||||
charge_mode: None,
|
||||
state: crate::state::steam_deck::Battery::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TBattery for Battery {
|
||||
fn limits(&self) -> crate::api::BatteryLimits {
|
||||
let max = Self::max();
|
||||
let min = Self::min();
|
||||
crate::api::BatteryLimits {
|
||||
charge_current: Some(RangeLimit{
|
||||
min: min.charge_rate.unwrap(),
|
||||
max: max.charge_rate.unwrap(),
|
||||
min: self.limits.charge_rate.min,
|
||||
max: self.limits.charge_rate.max
|
||||
}),
|
||||
charge_current_step: 50,
|
||||
charge_modes: vec!["normal".to_owned(), "discharge".to_owned(), "idle".to_owned()],
|
||||
|
|
|
@ -2,9 +2,10 @@ use std::convert::Into;
|
|||
|
||||
use crate::api::RangeLimit;
|
||||
use crate::settings::MinMax;
|
||||
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::settings::{OnResume, OnSet, SettingError};
|
||||
use crate::settings::{TCpus, TCpu};
|
||||
use crate::persist::CpuJson;
|
||||
use super::oc_limits::{OverclockLimits, CpusLimits, CpuLimits};
|
||||
|
||||
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
|
||||
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
|
||||
|
@ -14,6 +15,8 @@ pub struct Cpus {
|
|||
pub cpus: Vec<Cpu>,
|
||||
pub smt: bool,
|
||||
pub smt_capable: bool,
|
||||
#[allow(dead_code)] // in case this may be useful in the future
|
||||
pub(super) limits: CpusLimits,
|
||||
}
|
||||
|
||||
impl OnSet for Cpus {
|
||||
|
@ -81,28 +84,32 @@ impl Cpus {
|
|||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
let oc_limits = OverclockLimits::load_or_default().cpus;
|
||||
if let Some(max_cpu) = Self::cpu_count() {
|
||||
let mut sys_cpus = Vec::with_capacity(max_cpu);
|
||||
for i in 0..max_cpu {
|
||||
sys_cpus.push(Cpu::from_sys(i));
|
||||
sys_cpus.push(Cpu::from_sys(i, oc_limits.cpus.get(i).map(|x| x.to_owned()).unwrap_or_default()));
|
||||
}
|
||||
let (smt_status, can_smt) = Self::system_smt_capabilities();
|
||||
Self {
|
||||
cpus: sys_cpus,
|
||||
smt: smt_status,
|
||||
smt_capable: can_smt,
|
||||
limits: oc_limits,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
cpus: vec![],
|
||||
smt: false,
|
||||
smt_capable: false,
|
||||
limits: oc_limits,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_json(mut other: Vec<CpuJson>, version: u64) -> Self {
|
||||
let oc_limits = OverclockLimits::load_or_default().cpus;
|
||||
let (_, can_smt) = Self::system_smt_capabilities();
|
||||
let mut result = Vec::with_capacity(other.len());
|
||||
let max_cpus = Self::cpu_count();
|
||||
|
@ -113,7 +120,7 @@ impl Cpus {
|
|||
break;
|
||||
}
|
||||
}
|
||||
result.push(Cpu::from_json(cpu, version, i));
|
||||
result.push(Cpu::from_json(cpu, version, i, oc_limits.cpus.get(i).map(|x| x.to_owned()).unwrap_or_default()));
|
||||
}
|
||||
if let Some(max_cpus) = max_cpus {
|
||||
if result.len() != max_cpus {
|
||||
|
@ -127,6 +134,7 @@ impl Cpus {
|
|||
cpus: result,
|
||||
smt: true,
|
||||
smt_capable: can_smt,
|
||||
limits: oc_limits,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,6 +174,7 @@ pub struct Cpu {
|
|||
pub online: bool,
|
||||
pub clock_limits: Option<MinMax<u64>>,
|
||||
pub governor: String,
|
||||
limits: CpuLimits,
|
||||
index: usize,
|
||||
state: crate::state::steam_deck::Cpu,
|
||||
}
|
||||
|
@ -175,12 +184,13 @@ const CPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force
|
|||
|
||||
impl Cpu {
|
||||
#[inline]
|
||||
pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
|
||||
fn from_json(other: CpuJson, version: u64, i: usize, oc_limits: CpuLimits) -> Self {
|
||||
match version {
|
||||
0 => Self {
|
||||
online: other.online,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
governor: other.governor,
|
||||
limits: oc_limits,
|
||||
index: i,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
},
|
||||
|
@ -188,6 +198,7 @@ impl Cpu {
|
|||
online: other.online,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
governor: other.governor,
|
||||
limits: oc_limits,
|
||||
index: i,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
},
|
||||
|
@ -250,7 +261,7 @@ impl Cpu {
|
|||
// disable manual clock limits
|
||||
log::debug!("Setting CPU {} to default clockspeed", self.index);
|
||||
// max clock
|
||||
let payload_max = format!("p {} 1 {}\n", self.index / 2, Self::max().clock_limits.unwrap().max);
|
||||
let payload_max = format!("p {} 1 {}\n", self.index / 2, self.limits.clock_max.max);
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
|
@ -261,7 +272,7 @@ impl Cpu {
|
|||
},
|
||||
)?;
|
||||
// min clock
|
||||
let payload_min = format!("p {} 0 {}\n", self.index / 2, Self::min().clock_limits.unwrap().min);
|
||||
let payload_min = format!("p {} 0 {}\n", self.index / 2, self.limits.clock_min.min);
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
|
@ -297,41 +308,33 @@ impl Cpu {
|
|||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(clock_limits) = &mut self.clock_limits {
|
||||
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||
clock_limits.min = clock_limits.min.clamp(self.limits.clock_min.min, self.limits.clock_min.max);
|
||||
clock_limits.max = clock_limits.max.clamp(self.limits.clock_max.min, self.limits.clock_max.max);
|
||||
}
|
||||
}
|
||||
|
||||
fn from_sys(cpu_index: usize) -> Self {
|
||||
fn from_sys(cpu_index: usize, oc_limits: CpuLimits) -> Self {
|
||||
Self {
|
||||
online: usdpl_back::api::files::read_single(cpu_online_path(cpu_index)).unwrap_or(1u8) != 0,
|
||||
clock_limits: None,
|
||||
governor: usdpl_back::api::files::read_single(cpu_governor_path(cpu_index))
|
||||
.unwrap_or("schedutil".to_owned()),
|
||||
limits: oc_limits,
|
||||
index: cpu_index,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn limits(&self) -> crate::api::CpuLimits {
|
||||
let max = Self::max();
|
||||
let max_clocks = max.clock_limits.unwrap();
|
||||
|
||||
let min = Self::min();
|
||||
let min_clocks = min.clock_limits.unwrap();
|
||||
crate::api::CpuLimits {
|
||||
clock_min_limits: Some(RangeLimit {
|
||||
min: min_clocks.min,
|
||||
max: max_clocks.min
|
||||
min: self.limits.clock_min.min,
|
||||
max: self.limits.clock_min.max
|
||||
}),
|
||||
clock_max_limits: Some(RangeLimit {
|
||||
min: min_clocks.max,
|
||||
max: max_clocks.max
|
||||
min: self.limits.clock_max.min,
|
||||
max: self.limits.clock_max.max
|
||||
}),
|
||||
clock_step: 100,
|
||||
governors: self.governors(),
|
||||
|
@ -404,33 +407,6 @@ impl TCpu for Cpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Cpu {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
online: true,
|
||||
clock_limits: Some(MinMax {
|
||||
max: 3500,
|
||||
min: 3500,
|
||||
}),
|
||||
governor: "schedutil".to_owned(),
|
||||
index: usize::MAX,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
online: false,
|
||||
clock_limits: Some(MinMax { max: 500, min: 1400 }),
|
||||
governor: "schedutil".to_owned(),
|
||||
index: usize::MIN,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cpu_online_path(index: usize) -> String {
|
||||
format!("/sys/devices/system/cpu/cpu{}/online", index)
|
||||
|
|
|
@ -2,9 +2,10 @@ use std::convert::Into;
|
|||
|
||||
use crate::api::RangeLimit;
|
||||
use crate::settings::MinMax;
|
||||
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::settings::{OnResume, OnSet, SettingError};
|
||||
use crate::settings::TGpu;
|
||||
use crate::persist::GpuJson;
|
||||
use super::oc_limits::{OverclockLimits, GpuLimits};
|
||||
|
||||
const SLOW_PPT: u8 = 1;
|
||||
const FAST_PPT: u8 = 2;
|
||||
|
@ -15,6 +16,7 @@ pub struct Gpu {
|
|||
pub slow_ppt: Option<u64>,
|
||||
pub clock_limits: Option<MinMax<u64>>,
|
||||
pub slow_memory: bool,
|
||||
limits: GpuLimits,
|
||||
state: crate::state::steam_deck::Gpu,
|
||||
}
|
||||
|
||||
|
@ -26,12 +28,14 @@ const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk
|
|||
impl Gpu {
|
||||
#[inline]
|
||||
pub fn from_json(other: GpuJson, version: u64) -> Self {
|
||||
let oc_limits = OverclockLimits::load_or_default().gpu;
|
||||
match version {
|
||||
0 => Self {
|
||||
fast_ppt: other.fast_ppt,
|
||||
slow_ppt: other.slow_ppt,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
slow_memory: other.slow_memory,
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
},
|
||||
_ => Self {
|
||||
|
@ -39,6 +43,7 @@ impl Gpu {
|
|||
slow_ppt: other.slow_ppt,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
slow_memory: other.slow_memory,
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
},
|
||||
}
|
||||
|
@ -120,7 +125,7 @@ impl Gpu {
|
|||
self.state.clock_limits_set = false;
|
||||
// disable manual clock limits
|
||||
// max clock
|
||||
let payload_max = format!("s 1 {}\n", Self::max().clock_limits.unwrap().max);
|
||||
let payload_max = format!("s 1 {}\n", self.limits.clock_max.max);
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
|
@ -131,7 +136,7 @@ impl Gpu {
|
|||
},
|
||||
)?;
|
||||
// min clock
|
||||
let payload_min = format!("s 0 {}\n", Self::min().clock_limits.unwrap().min);
|
||||
let payload_min = format!("s 0 {}\n", self.limits.clock_min.min);
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
|
@ -154,34 +159,32 @@ impl Gpu {
|
|||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(fast_ppt) = &mut self.fast_ppt {
|
||||
*fast_ppt = (*fast_ppt).clamp(
|
||||
*min.fast_ppt.as_ref().unwrap(),
|
||||
*max.fast_ppt.as_ref().unwrap(),
|
||||
self.limits.fast_ppt.min,
|
||||
self.limits.fast_ppt.max,
|
||||
);
|
||||
}
|
||||
if let Some(slow_ppt) = &mut self.slow_ppt {
|
||||
*slow_ppt = (*slow_ppt).clamp(
|
||||
*min.slow_ppt.as_ref().unwrap(),
|
||||
*max.slow_ppt.as_ref().unwrap(),
|
||||
self.limits.slow_ppt.min,
|
||||
self.limits.slow_ppt.max,
|
||||
);
|
||||
}
|
||||
if let Some(clock_limits) = &mut self.clock_limits {
|
||||
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||
clock_limits.min = clock_limits.min.clamp(self.limits.clock_min.min, self.limits.clock_min.max);
|
||||
clock_limits.max = clock_limits.max.clamp(self.limits.clock_max.min, self.limits.clock_max.max);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
let oc_limits = OverclockLimits::load_or_default().gpu;
|
||||
Self {
|
||||
fast_ppt: None,
|
||||
slow_ppt: None,
|
||||
clock_limits: None,
|
||||
slow_memory: false,
|
||||
limits: oc_limits,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
}
|
||||
}
|
||||
|
@ -214,62 +217,30 @@ impl OnResume for Gpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Gpu {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
fast_ppt: Some(30_000_000),
|
||||
slow_ppt: Some(29_000_000),
|
||||
clock_limits: Some(MinMax {
|
||||
min: 1600,
|
||||
max: 1600,
|
||||
}),
|
||||
slow_memory: false,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
fast_ppt: Some(0),
|
||||
slow_ppt: Some(1000000),
|
||||
clock_limits: Some(MinMax { min: 200, max: 200 }),
|
||||
slow_memory: true,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PPT_DIVISOR: u64 = 1_000_000;
|
||||
|
||||
impl TGpu for Gpu {
|
||||
fn limits(&self) -> crate::api::GpuLimits {
|
||||
let max = Self::max();
|
||||
let max_clock_limits = max.clock_limits.unwrap();
|
||||
|
||||
let min = Self::min();
|
||||
let min_clock_limits = min.clock_limits.unwrap();
|
||||
crate::api::GpuLimits {
|
||||
fast_ppt_limits: Some(RangeLimit {
|
||||
min: min.fast_ppt.unwrap() / PPT_DIVISOR,
|
||||
max: max.fast_ppt.unwrap() / PPT_DIVISOR,
|
||||
min: self.limits.fast_ppt.min / PPT_DIVISOR,
|
||||
max: self.limits.fast_ppt.max / PPT_DIVISOR,
|
||||
}),
|
||||
slow_ppt_limits: Some(RangeLimit {
|
||||
min: min.slow_ppt.unwrap() / PPT_DIVISOR,
|
||||
max: max.slow_ppt.unwrap() / PPT_DIVISOR,
|
||||
min: self.limits.slow_ppt.min / PPT_DIVISOR,
|
||||
max: self.limits.slow_ppt.max / PPT_DIVISOR,
|
||||
}),
|
||||
ppt_step: 1,
|
||||
tdp_limits: None,
|
||||
tdp_boost_limits: None,
|
||||
tdp_step: 42,
|
||||
clock_min_limits: Some(RangeLimit {
|
||||
min: min_clock_limits.min,
|
||||
max: max_clock_limits.max,
|
||||
min: self.limits.clock_min.min,
|
||||
max: self.limits.clock_min.max,
|
||||
}),
|
||||
clock_max_limits: Some(RangeLimit {
|
||||
min: min_clock_limits.min,
|
||||
max: max_clock_limits.max,
|
||||
min: self.limits.clock_max.min,
|
||||
max: self.limits.clock_max.max,
|
||||
}),
|
||||
clock_step: 100,
|
||||
memory_control_capable: true,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
mod battery;
|
||||
mod cpu;
|
||||
mod gpu;
|
||||
mod oc_limits;
|
||||
mod util;
|
||||
|
||||
pub use battery::Battery;
|
||||
|
|
115
backend/src/settings/steam_deck/oc_limits.rs
Normal file
115
backend/src/settings/steam_deck/oc_limits.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(super) struct MinMaxLimits<T> {
|
||||
pub min: T,
|
||||
pub max: T,
|
||||
}
|
||||
|
||||
const OC_LIMITS_FILEPATH: &str = "./pt_oc.json";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub(super) struct OverclockLimits {
|
||||
pub battery: BatteryLimits,
|
||||
pub cpus: CpusLimits,
|
||||
pub gpu: GpuLimits,
|
||||
}
|
||||
|
||||
impl Default for OverclockLimits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
battery: BatteryLimits::default(),
|
||||
cpus: CpusLimits::default(),
|
||||
gpu: GpuLimits::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OverclockLimits {
|
||||
pub fn load_or_default() -> Self {
|
||||
let path = std::path::Path::new(OC_LIMITS_FILEPATH);
|
||||
if path.exists() {
|
||||
log::info!("Steam Deck limits file {} found", path.display());
|
||||
let mut file = match std::fs::File::open(&path) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
log::warn!("Steam Deck limits file {} err: {} (using default fallback)", path.display(), e);
|
||||
return Self::default();
|
||||
},
|
||||
};
|
||||
match serde_json::from_reader(&mut file) {
|
||||
Ok(result) => {
|
||||
log::debug!("Steam Deck limits file {} successfully loaded", path.display());
|
||||
result
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("Steam Deck limits file {} json err: {} (using default fallback)", path.display(), e);
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::info!("Steam Deck limits file {} not found (using default fallback)", path.display());
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(super) struct BatteryLimits {
|
||||
pub charge_rate: MinMaxLimits<u64>,
|
||||
}
|
||||
|
||||
impl Default for BatteryLimits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
charge_rate: MinMaxLimits { min: 250, max: 2500 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(super) struct CpusLimits {
|
||||
pub cpus: Vec<CpuLimits>,
|
||||
}
|
||||
|
||||
impl Default for CpusLimits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cpus: [(); 8].iter().map(|_| CpuLimits::default()).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(super) struct CpuLimits {
|
||||
pub clock_min: MinMaxLimits<u64>,
|
||||
pub clock_max: MinMaxLimits<u64>,
|
||||
}
|
||||
|
||||
impl Default for CpuLimits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
clock_min: MinMaxLimits { min: 1400, max: 3500 },
|
||||
clock_max: MinMaxLimits { min: 500, max: 3500 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(super) struct GpuLimits {
|
||||
pub fast_ppt: MinMaxLimits<u64>,
|
||||
pub slow_ppt: MinMaxLimits<u64>,
|
||||
pub clock_min: MinMaxLimits<u64>,
|
||||
pub clock_max: MinMaxLimits<u64>,
|
||||
}
|
||||
|
||||
impl Default for GpuLimits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fast_ppt: MinMaxLimits { min: 1000000, max: 30_000_000 },
|
||||
slow_ppt: MinMaxLimits { min: 1000000, max: 29_000_000 },
|
||||
clock_min: MinMaxLimits { min: 200, max: 1600 },
|
||||
clock_max: MinMaxLimits { min: 200, max: 1600 }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,428 +0,0 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use crate::api::RangeLimit;
|
||||
use crate::settings::MinMax;
|
||||
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::settings::{TCpus, TCpu};
|
||||
use crate::persist::CpuJson;
|
||||
|
||||
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
|
||||
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cpus {
|
||||
pub cpus: Vec<Cpu>,
|
||||
pub smt: bool,
|
||||
pub smt_capable: bool,
|
||||
}
|
||||
|
||||
impl OnSet for Cpus {
|
||||
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||
if self.smt_capable {
|
||||
// toggle SMT
|
||||
if self.smt {
|
||||
usdpl_back::api::files::write_single(CPU_SMT_PATH, "on").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `on` to `{}`: {}",
|
||||
CPU_SMT_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
} else {
|
||||
usdpl_back::api::files::write_single(CPU_SMT_PATH, "off").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `off` to `{}`: {}",
|
||||
CPU_SMT_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
}
|
||||
for (i, cpu) in self.cpus.as_mut_slice().iter_mut().enumerate() {
|
||||
cpu.state.do_set_online = self.smt || i % 2 == 0;
|
||||
cpu.on_set()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl OnResume for Cpus {
|
||||
fn on_resume(&self) -> Result<(), SettingError> {
|
||||
for cpu in &self.cpus {
|
||||
cpu.on_resume()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Cpus {
|
||||
pub fn cpu_count() -> Option<usize> {
|
||||
let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH)
|
||||
.unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */);
|
||||
if let Some(dash_index) = data.find('-') {
|
||||
let data = data.split_off(dash_index + 1);
|
||||
if let Ok(max_cpu) = data.parse::<usize>() {
|
||||
return Some(max_cpu + 1);
|
||||
}
|
||||
}
|
||||
log::warn!("Failed to parse CPU info from kernel, is Tux evil?");
|
||||
None
|
||||
}
|
||||
|
||||
fn system_smt_capabilities() -> (bool, bool) {
|
||||
match usdpl_back::api::files::read_single::<_, String, _>(CPU_SMT_PATH) {
|
||||
Ok(val) => (val.trim().to_lowercase() == "on", true),
|
||||
Err(_) => (false, false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
if let Some(max_cpu) = Self::cpu_count() {
|
||||
let mut sys_cpus = Vec::with_capacity(max_cpu);
|
||||
for i in 0..max_cpu {
|
||||
sys_cpus.push(Cpu::from_sys(i));
|
||||
}
|
||||
let (smt_status, can_smt) = Self::system_smt_capabilities();
|
||||
Self {
|
||||
cpus: sys_cpus,
|
||||
smt: smt_status,
|
||||
smt_capable: can_smt,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
cpus: vec![],
|
||||
smt: false,
|
||||
smt_capable: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_json(mut other: Vec<CpuJson>, version: u64) -> Self {
|
||||
let (_, can_smt) = Self::system_smt_capabilities();
|
||||
let mut result = Vec::with_capacity(other.len());
|
||||
let max_cpus = Self::cpu_count();
|
||||
for (i, cpu) in other.drain(..).enumerate() {
|
||||
// prevent having more CPUs than available
|
||||
if let Some(max_cpus) = max_cpus {
|
||||
if i == max_cpus {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.push(Cpu::from_json(cpu, version, i));
|
||||
}
|
||||
if let Some(max_cpus) = max_cpus {
|
||||
if result.len() != max_cpus {
|
||||
let mut sys_cpus = Cpus::system_default();
|
||||
for i in result.len()..sys_cpus.cpus.len() {
|
||||
result.push(sys_cpus.cpus.remove(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
cpus: result,
|
||||
smt: true,
|
||||
smt_capable: can_smt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TCpus for Cpus {
|
||||
fn limits(&self) -> crate::api::CpusLimits {
|
||||
crate::api::CpusLimits {
|
||||
cpus: self.cpus.iter().map(|x| x.limits()).collect(),
|
||||
count: self.cpus.len(),
|
||||
smt_capable: self.smt_capable,
|
||||
}
|
||||
}
|
||||
|
||||
fn json(&self) -> Vec<crate::persist::CpuJson> {
|
||||
self.cpus.iter().map(|x| x.to_owned().into()).collect()
|
||||
}
|
||||
|
||||
fn cpus(&mut self) -> Vec<&mut dyn TCpu> {
|
||||
self.cpus.iter_mut().map(|x| x as &mut dyn TCpu).collect()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.cpus.len()
|
||||
}
|
||||
|
||||
fn smt(&mut self) -> &'_ mut bool {
|
||||
&mut self.smt
|
||||
}
|
||||
|
||||
fn provider(&self) -> crate::persist::DriverJson {
|
||||
crate::persist::DriverJson::SteamDeckAdvance
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cpu {
|
||||
pub online: bool,
|
||||
pub clock_limits: Option<MinMax<u64>>,
|
||||
pub governor: String,
|
||||
index: usize,
|
||||
state: crate::state::steam_deck::Cpu,
|
||||
}
|
||||
|
||||
const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||
const CPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
||||
|
||||
impl Cpu {
|
||||
#[inline]
|
||||
pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
|
||||
match version {
|
||||
0 => Self {
|
||||
online: other.online,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
governor: other.governor,
|
||||
index: i,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
},
|
||||
_ => Self {
|
||||
online: other.online,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
governor: other.governor,
|
||||
index: i,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||
// set cpu online/offline
|
||||
if self.index != 0 && self.state.do_set_online { // cpu0 cannot be disabled
|
||||
let online_path = cpu_online_path(self.index);
|
||||
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", &online_path, e),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
// set clock limits
|
||||
log::debug!("Setting {} to manual", CPU_FORCE_LIMITS_PATH);
|
||||
let mode: String = usdpl_back::api::files::read_single(CPU_FORCE_LIMITS_PATH.to_owned()).unwrap();
|
||||
if mode != "manual" {
|
||||
// set manual control
|
||||
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `manual` to `{}`: {}",
|
||||
CPU_FORCE_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
if let Some(clock_limits) = &self.clock_limits {
|
||||
log::debug!("Setting CPU {} (min, max) clockspeed to ({}, {})", self.index, clock_limits.min, clock_limits.max);
|
||||
self.state.clock_limits_set = true;
|
||||
// max clock
|
||||
let payload_max = format!("p {} 1 {}\n", self.index / 2, clock_limits.max);
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
let payload_min = format!("p {} 0 {}\n", self.index / 2, clock_limits.min);
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
},
|
||||
)?;
|
||||
} else if self.state.clock_limits_set || self.state.is_resuming {
|
||||
self.state.clock_limits_set = false;
|
||||
// disable manual clock limits
|
||||
log::debug!("Setting CPU {} to default clockspeed", self.index);
|
||||
// max clock
|
||||
let payload_max = format!("p {} 1 {}\n", self.index / 2, Self::max().clock_limits.unwrap().max);
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
let payload_min = format!("p {} 0 {}\n", self.index / 2, Self::min().clock_limits.unwrap().min);
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
// commit changes
|
||||
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
|
||||
// set governor
|
||||
if self.index == 0 || self.online {
|
||||
let governor_path = cpu_governor_path(self.index);
|
||||
usdpl_back::api::files::write_single(&governor_path, &self.governor).map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&self.governor, &governor_path, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Cpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(clock_limits) = &mut self.clock_limits {
|
||||
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||
}
|
||||
}
|
||||
|
||||
fn from_sys(cpu_index: usize) -> Self {
|
||||
Self {
|
||||
online: usdpl_back::api::files::read_single(cpu_online_path(cpu_index)).unwrap_or(1u8) != 0,
|
||||
clock_limits: None,
|
||||
governor: usdpl_back::api::files::read_single(cpu_governor_path(cpu_index))
|
||||
.unwrap_or("schedutil".to_owned()),
|
||||
index: cpu_index,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn limits(&self) -> crate::api::CpuLimits {
|
||||
let max = Self::max();
|
||||
let max_clocks = max.clock_limits.unwrap();
|
||||
|
||||
let min = Self::min();
|
||||
let min_clocks = min.clock_limits.unwrap();
|
||||
crate::api::CpuLimits {
|
||||
clock_min_limits: Some(RangeLimit {
|
||||
min: min_clocks.min,
|
||||
max: max_clocks.min
|
||||
}),
|
||||
clock_max_limits: Some(RangeLimit {
|
||||
min: min_clocks.max,
|
||||
max: max_clocks.max
|
||||
}),
|
||||
clock_step: 100,
|
||||
governors: vec![], // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<CpuJson> for Cpu {
|
||||
#[inline]
|
||||
fn into(self) -> CpuJson {
|
||||
CpuJson {
|
||||
online: self.online,
|
||||
clock_limits: self.clock_limits.map(|x| x.into()),
|
||||
governor: self.governor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OnSet for Cpu {
|
||||
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||
self.clamp_all();
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl OnResume for Cpu {
|
||||
fn on_resume(&self) -> Result<(), SettingError> {
|
||||
let mut copy = self.clone();
|
||||
copy.state.is_resuming = true;
|
||||
copy.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl TCpu for Cpu {
|
||||
fn online(&mut self) -> &mut bool {
|
||||
&mut self.online
|
||||
}
|
||||
|
||||
fn governor(&mut self, governor: String) {
|
||||
self.governor = governor;
|
||||
}
|
||||
|
||||
fn get_governor(&self) -> &'_ str {
|
||||
&self.governor
|
||||
}
|
||||
|
||||
fn clock_limits(&mut self, limits: Option<MinMax<u64>>) {
|
||||
self.clock_limits = limits;
|
||||
}
|
||||
|
||||
fn get_clock_limits(&self) -> Option<&MinMax<u64>> {
|
||||
self.clock_limits.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Cpu {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
online: true,
|
||||
clock_limits: Some(MinMax {
|
||||
max: 3500,
|
||||
min: 3500,
|
||||
}),
|
||||
governor: "schedutil".to_owned(),
|
||||
index: usize::MAX,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
online: false,
|
||||
clock_limits: Some(MinMax { max: 500, min: 1400 }),
|
||||
governor: "schedutil".to_owned(),
|
||||
index: usize::MIN,
|
||||
state: crate::state::steam_deck::Cpu::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cpu_online_path(index: usize) -> String {
|
||||
format!("/sys/devices/system/cpu/cpu{}/online", index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cpu_governor_path(index: usize) -> String {
|
||||
format!(
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_governor",
|
||||
index
|
||||
)
|
||||
}
|
|
@ -1,310 +0,0 @@
|
|||
use std::convert::Into;
|
||||
|
||||
use crate::api::RangeLimit;
|
||||
use crate::settings::MinMax;
|
||||
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange};
|
||||
use crate::settings::TGpu;
|
||||
use crate::persist::GpuJson;
|
||||
|
||||
const SLOW_PPT: u8 = 1;
|
||||
const FAST_PPT: u8 = 2;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Gpu {
|
||||
pub fast_ppt: Option<u64>,
|
||||
pub slow_ppt: Option<u64>,
|
||||
pub clock_limits: Option<MinMax<u64>>,
|
||||
pub slow_memory: bool,
|
||||
state: crate::state::steam_deck::Gpu,
|
||||
}
|
||||
|
||||
// same as CPU
|
||||
const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||
const GPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
||||
const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk";
|
||||
|
||||
impl Gpu {
|
||||
#[inline]
|
||||
pub fn from_json(other: GpuJson, version: u64) -> Self {
|
||||
match version {
|
||||
0 => Self {
|
||||
fast_ppt: other.fast_ppt,
|
||||
slow_ppt: other.slow_ppt,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
slow_memory: other.slow_memory,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
},
|
||||
_ => Self {
|
||||
fast_ppt: other.fast_ppt,
|
||||
slow_ppt: other.slow_ppt,
|
||||
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||
slow_memory: other.slow_memory,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_all(&mut self) -> Result<(), SettingError> {
|
||||
// set fast PPT
|
||||
if let Some(fast_ppt) = &self.fast_ppt {
|
||||
let fast_ppt_path = gpu_power_path(FAST_PPT);
|
||||
usdpl_back::api::files::write_single(&fast_ppt_path, fast_ppt).map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
fast_ppt, &fast_ppt_path, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
// set slow PPT
|
||||
if let Some(slow_ppt) = &self.slow_ppt {
|
||||
let slow_ppt_path = gpu_power_path(SLOW_PPT);
|
||||
usdpl_back::api::files::write_single(&slow_ppt_path, slow_ppt).map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
slow_ppt, &slow_ppt_path, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
// settings using force_performance_level
|
||||
let mode: String = usdpl_back::api::files::read_single(GPU_FORCE_LIMITS_PATH.to_owned()).unwrap();
|
||||
if mode != "manual" {
|
||||
// set manual control
|
||||
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `manual` to `{}`: {}",
|
||||
GPU_FORCE_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
// enable/disable downclock of GPU memory (to 400Mhz?)
|
||||
usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8)
|
||||
.map_err(|e| SettingError {
|
||||
msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
})?;
|
||||
if let Some(clock_limits) = &self.clock_limits {
|
||||
// set clock limits
|
||||
self.state.clock_limits_set = true;
|
||||
// max clock
|
||||
let payload_max = format!("s 1 {}\n", clock_limits.max);
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
let payload_min = format!("s 0 {}\n", clock_limits.min);
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_min, GPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
},
|
||||
)?;
|
||||
} else if self.state.clock_limits_set || self.state.is_resuming {
|
||||
self.state.clock_limits_set = false;
|
||||
// disable manual clock limits
|
||||
// max clock
|
||||
let payload_max = format!("s 1 {}\n", Self::max().clock_limits.unwrap().max);
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
},
|
||||
)?;
|
||||
// min clock
|
||||
let payload_min = format!("s 0 {}\n", Self::min().clock_limits.unwrap().min);
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||
|e| SettingError {
|
||||
msg: format!(
|
||||
"Failed to write `{}` to `{}`: {}",
|
||||
&payload_min, GPU_CLOCK_LIMITS_PATH, e
|
||||
),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
// commit changes
|
||||
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| {
|
||||
SettingError {
|
||||
msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e),
|
||||
setting: crate::settings::SettingVariant::Gpu,
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clamp_all(&mut self) {
|
||||
let min = Self::min();
|
||||
let max = Self::max();
|
||||
if let Some(fast_ppt) = &mut self.fast_ppt {
|
||||
*fast_ppt = (*fast_ppt).clamp(
|
||||
*min.fast_ppt.as_ref().unwrap(),
|
||||
*max.fast_ppt.as_ref().unwrap(),
|
||||
);
|
||||
}
|
||||
if let Some(slow_ppt) = &mut self.slow_ppt {
|
||||
*slow_ppt = (*slow_ppt).clamp(
|
||||
*min.slow_ppt.as_ref().unwrap(),
|
||||
*max.slow_ppt.as_ref().unwrap(),
|
||||
);
|
||||
}
|
||||
if let Some(clock_limits) = &mut self.clock_limits {
|
||||
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn system_default() -> Self {
|
||||
Self {
|
||||
fast_ppt: None,
|
||||
slow_ppt: None,
|
||||
clock_limits: None,
|
||||
slow_memory: false,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<GpuJson> for Gpu {
|
||||
#[inline]
|
||||
fn into(self) -> GpuJson {
|
||||
GpuJson {
|
||||
fast_ppt: self.fast_ppt,
|
||||
slow_ppt: self.slow_ppt,
|
||||
clock_limits: self.clock_limits.map(|x| x.into()),
|
||||
slow_memory: self.slow_memory,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OnSet for Gpu {
|
||||
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||
self.clamp_all();
|
||||
self.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl OnResume for Gpu {
|
||||
fn on_resume(&self) -> Result<(), SettingError> {
|
||||
let mut copy = self.clone();
|
||||
copy.state.is_resuming = true;
|
||||
copy.set_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsRange for Gpu {
|
||||
#[inline]
|
||||
fn max() -> Self {
|
||||
Self {
|
||||
fast_ppt: Some(30_000_000),
|
||||
slow_ppt: Some(29_000_000),
|
||||
clock_limits: Some(MinMax {
|
||||
min: 1600,
|
||||
max: 1600,
|
||||
}),
|
||||
slow_memory: false,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn min() -> Self {
|
||||
Self {
|
||||
fast_ppt: Some(0),
|
||||
slow_ppt: Some(1000000),
|
||||
clock_limits: Some(MinMax { min: 200, max: 200 }),
|
||||
slow_memory: true,
|
||||
state: crate::state::steam_deck::Gpu::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TGpu for Gpu {
|
||||
fn limits(&self) -> crate::api::GpuLimits {
|
||||
let max = Self::max();
|
||||
let max_clock_limits = max.clock_limits.unwrap();
|
||||
|
||||
let min = Self::min();
|
||||
let min_clock_limits = min.clock_limits.unwrap();
|
||||
crate::api::GpuLimits {
|
||||
fast_ppt_limits: Some(RangeLimit {
|
||||
min: min.fast_ppt.unwrap(),
|
||||
max: max.fast_ppt.unwrap(),
|
||||
}),
|
||||
slow_ppt_limits: Some(RangeLimit {
|
||||
min: min.slow_ppt.unwrap(),
|
||||
max: max.slow_ppt.unwrap(),
|
||||
}),
|
||||
ppt_step: 1_000_000,
|
||||
tdp_limits: None,
|
||||
tdp_boost_limits: None,
|
||||
tdp_step: 42,
|
||||
clock_min_limits: Some(RangeLimit {
|
||||
min: min_clock_limits.min,
|
||||
max: max_clock_limits.max,
|
||||
}),
|
||||
clock_max_limits: Some(RangeLimit {
|
||||
min: min_clock_limits.min,
|
||||
max: max_clock_limits.max,
|
||||
}),
|
||||
clock_step: 100,
|
||||
memory_control_capable: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn json(&self) -> crate::persist::GpuJson {
|
||||
self.clone().into()
|
||||
}
|
||||
|
||||
fn ppt(&mut self, fast: Option<u64>, slow: Option<u64>) {
|
||||
self.fast_ppt = fast;
|
||||
self.slow_ppt = slow;
|
||||
}
|
||||
|
||||
fn get_ppt(&self) -> (Option<u64>, Option<u64>) {
|
||||
(self.fast_ppt, self.slow_ppt)
|
||||
}
|
||||
|
||||
fn clock_limits(&mut self, limits: Option<MinMax<u64>>) {
|
||||
self.clock_limits = limits;
|
||||
}
|
||||
|
||||
fn get_clock_limits(&self) -> Option<&MinMax<u64>> {
|
||||
self.clock_limits.as_ref()
|
||||
}
|
||||
|
||||
fn slow_memory(&mut self) -> &mut bool {
|
||||
&mut self.slow_memory
|
||||
}
|
||||
|
||||
fn provider(&self) -> crate::persist::DriverJson {
|
||||
crate::persist::DriverJson::SteamDeckAdvance
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gpu_power_path(power_number: u8) -> String {
|
||||
format!("/sys/class/hwmon/hwmon4/power{}_cap", power_number)
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
//mod battery;
|
||||
mod cpu;
|
||||
mod gpu;
|
||||
|
||||
//pub use battery::Battery;
|
||||
pub use cpu::{Cpu, Cpus};
|
||||
pub use gpu::Gpu;
|
Loading…
Reference in a new issue