Combine SD advance mode back into regular SD driver, implement custom clock file support

This commit is contained in:
NGnius (Graham) 2022-12-30 23:27:22 -05:00
parent 5fe4e9d15d
commit 1dc0acca8e
15 changed files with 318 additions and 884 deletions

89
backend/limits_core/Cargo.lock generated Normal file
View 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"

View file

@ -14,6 +14,21 @@ impl Default for Base {
fn default() -> Self { fn default() -> Self {
Base { Base {
configs: vec![ 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 { super::Config {
name: "Steam Deck".to_owned(), name: "Steam Deck".to_owned(),
conditions: super::Conditions { conditions: super::Conditions {
@ -21,6 +36,7 @@ impl Default for Base {
cpuinfo: Some("model name\t: AMD Custom APU 0405\n".to_owned()), cpuinfo: Some("model name\t: AMD Custom APU 0405\n".to_owned()),
os: None, os: None,
command: None, command: None,
file_exists: None,
}, },
limits: vec![ limits: vec![
super::Limits::Cpu(super::CpuLimit::SteamDeck), super::Limits::Cpu(super::CpuLimit::SteamDeck),
@ -35,6 +51,7 @@ impl Default for Base {
cpuinfo: None, cpuinfo: None,
os: None, os: None,
command: None, command: None,
file_exists: None,
}, },
limits: vec![ limits: vec![
super::Limits::Cpu(super::CpuLimit::Unknown), super::Limits::Cpu(super::CpuLimit::Unknown),

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Conditions under which a config applies /// Conditions under which a config applies (ANDed together)
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Conditions { pub struct Conditions {
/// Regex pattern for dmidecode output /// Regex pattern for dmidecode output
@ -11,6 +11,8 @@ pub struct Conditions {
pub os: Option<String>, pub os: Option<String>,
/// Custom command to run, where an exit code of 0 means a successful match /// Custom command to run, where an exit code of 0 means a successful match
pub command: Option<String>, pub command: Option<String>,
/// Check if file exists
pub file_exists: Option<String>,
} }
impl Conditions { impl Conditions {

View file

@ -1,5 +1,26 @@
{ {
"configs": [ "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", "name": "Steam Deck",
"conditions": { "conditions": {

View file

@ -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), 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 matches {
if let Some(settings) = &settings_opt { 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) => { Limits::Cpu(cpus) => {
let cpu_driver: Box<dyn TCpus> = match 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::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::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)), 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) => { Limits::Gpu(gpu) => {
let driver: Box<dyn TGpu> = match 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::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::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)), 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) => { Limits::Cpu(cpus) => {
let cpu_driver: Box<dyn TCpus> = match cpus { let cpu_driver: Box<dyn TCpus> = match cpus {
CpuLimit::SteamDeck => Box::new(crate::settings::steam_deck::Cpus::system_default()), 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::Generic(x) => Box::new(crate::settings::generic::Cpus::from_limits(x)),
CpuLimit::Unknown => Box::new(crate::settings::unknown::Cpus::system_default()), 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) => { Limits::Gpu(gpu) => {
let driver: Box<dyn TGpu> = match gpu { let driver: Box<dyn TGpu> = match gpu {
GpuLimit::SteamDeck => Box::new(crate::settings::steam_deck::Gpu::system_default()), 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::Generic(x) => Box::new(crate::settings::generic::Gpu::from_limits(x)),
GpuLimit::Unknown => Box::new(crate::settings::unknown::Gpu::system_default()), GpuLimit::Unknown => Box::new(crate::settings::unknown::Gpu::system_default()),
}; };

View file

@ -40,6 +40,7 @@ impl Driver {
gpu: Box::new(super::steam_deck::Gpu::from_json(settings.gpu, 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)), 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 { DriverJson::SteamDeckAdvance => Ok(Self {
general: Box::new(General { general: Box::new(General {
persistent: settings.persistent, persistent: settings.persistent,
@ -47,8 +48,8 @@ impl Driver {
name: settings.name, name: settings.name,
driver: DriverJson::SteamDeckAdvance, driver: DriverJson::SteamDeckAdvance,
}), }),
cpus: Box::new(super::steam_deck_adv::Cpus::from_json(settings.cpus, settings.version)), cpus: Box::new(super::steam_deck::Cpus::from_json(settings.cpus, settings.version)),
gpu: Box::new(super::steam_deck_adv::Gpu::from_json(settings.gpu, 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)), battery: Box::new(super::steam_deck::Battery::from_json(settings.battery, settings.version)),
}), }),
DriverJson::Generic => Ok(Self { DriverJson::Generic => Ok(Self {

View file

@ -7,7 +7,6 @@ mod traits;
pub mod generic; pub mod generic;
pub mod steam_deck; pub mod steam_deck;
pub mod steam_deck_adv;
pub mod unknown; pub mod unknown;
pub use detect::{auto_detect0, auto_detect_provider, limits_worker::spawn as limits_worker_spawn}; pub use detect::{auto_detect0, auto_detect_provider, limits_worker::spawn as limits_worker_spawn};

View file

@ -1,15 +1,17 @@
use std::convert::Into; use std::convert::Into;
use crate::api::RangeLimit; use crate::api::RangeLimit;
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange}; use crate::settings::{OnResume, OnSet, SettingError};
use crate::settings::TBattery; use crate::settings::TBattery;
use crate::persist::BatteryJson; use crate::persist::BatteryJson;
use super::util::ChargeMode; use super::util::ChargeMode;
use super::oc_limits::{BatteryLimits, OverclockLimits};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Battery { pub struct Battery {
pub charge_rate: Option<u64>, pub charge_rate: Option<u64>,
pub charge_mode: Option<ChargeMode>, pub charge_mode: Option<ChargeMode>,
limits: BatteryLimits,
state: crate::state::steam_deck::Battery, state: crate::state::steam_deck::Battery,
} }
@ -24,15 +26,18 @@ const BATTERY_CHARGE_DESIGN_PATH: &str = "/sys/class/hwmon/hwmon2/device/charge_
impl Battery { impl Battery {
#[inline] #[inline]
pub fn from_json(other: BatteryJson, version: u64) -> Self { pub fn from_json(other: BatteryJson, version: u64) -> Self {
let oc_limits = OverclockLimits::load_or_default().battery;
match version { match version {
0 => Self { 0 => Self {
charge_rate: other.charge_rate, charge_rate: other.charge_rate,
charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(), charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(),
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(), state: crate::state::steam_deck::Battery::default(),
}, },
_ => Self { _ => Self {
charge_rate: other.charge_rate, charge_rate: other.charge_rate,
charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(), charge_mode: other.charge_mode.map(|x| Self::str_to_charge_mode(&x)).flatten(),
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(), state: crate::state::steam_deck::Battery::default(),
}, },
} }
@ -68,7 +73,7 @@ impl Battery {
)?; )?;
} else if self.state.charge_rate_set { } else if self.state.charge_rate_set {
self.state.charge_rate_set = false; 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 { |e| SettingError {
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e), msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
setting: crate::settings::SettingVariant::Battery, setting: crate::settings::SettingVariant::Battery,
@ -96,10 +101,8 @@ impl Battery {
} }
fn clamp_all(&mut self) { fn clamp_all(&mut self) {
let min = Self::min();
let max = Self::max();
if let Some(charge_rate) = &mut self.charge_rate { 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 { pub fn system_default() -> Self {
let oc_limits = OverclockLimits::load_or_default().battery;
Self { Self {
charge_rate: None, charge_rate: None,
charge_mode: None, charge_mode: None,
limits: oc_limits,
state: crate::state::steam_deck::Battery::default(), 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 { impl TBattery for Battery {
fn limits(&self) -> crate::api::BatteryLimits { fn limits(&self) -> crate::api::BatteryLimits {
let max = Self::max();
let min = Self::min();
crate::api::BatteryLimits { crate::api::BatteryLimits {
charge_current: Some(RangeLimit{ charge_current: Some(RangeLimit{
min: min.charge_rate.unwrap(), min: self.limits.charge_rate.min,
max: max.charge_rate.unwrap(), max: self.limits.charge_rate.max
}), }),
charge_current_step: 50, charge_current_step: 50,
charge_modes: vec!["normal".to_owned(), "discharge".to_owned(), "idle".to_owned()], charge_modes: vec!["normal".to_owned(), "discharge".to_owned(), "idle".to_owned()],

View file

@ -2,9 +2,10 @@ use std::convert::Into;
use crate::api::RangeLimit; use crate::api::RangeLimit;
use crate::settings::MinMax; use crate::settings::MinMax;
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange}; use crate::settings::{OnResume, OnSet, SettingError};
use crate::settings::{TCpus, TCpu}; use crate::settings::{TCpus, TCpu};
use crate::persist::CpuJson; use crate::persist::CpuJson;
use super::oc_limits::{OverclockLimits, CpusLimits, CpuLimits};
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present"; const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control"; const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control";
@ -14,6 +15,8 @@ pub struct Cpus {
pub cpus: Vec<Cpu>, pub cpus: Vec<Cpu>,
pub smt: bool, pub smt: bool,
pub smt_capable: 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 { impl OnSet for Cpus {
@ -81,28 +84,32 @@ impl Cpus {
} }
pub fn system_default() -> Self { pub fn system_default() -> Self {
let oc_limits = OverclockLimits::load_or_default().cpus;
if let Some(max_cpu) = Self::cpu_count() { if let Some(max_cpu) = Self::cpu_count() {
let mut sys_cpus = Vec::with_capacity(max_cpu); let mut sys_cpus = Vec::with_capacity(max_cpu);
for i in 0..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(); let (smt_status, can_smt) = Self::system_smt_capabilities();
Self { Self {
cpus: sys_cpus, cpus: sys_cpus,
smt: smt_status, smt: smt_status,
smt_capable: can_smt, smt_capable: can_smt,
limits: oc_limits,
} }
} else { } else {
Self { Self {
cpus: vec![], cpus: vec![],
smt: false, smt: false,
smt_capable: false, smt_capable: false,
limits: oc_limits,
} }
} }
} }
#[inline] #[inline]
pub fn from_json(mut other: Vec<CpuJson>, version: u64) -> Self { 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 (_, can_smt) = Self::system_smt_capabilities();
let mut result = Vec::with_capacity(other.len()); let mut result = Vec::with_capacity(other.len());
let max_cpus = Self::cpu_count(); let max_cpus = Self::cpu_count();
@ -113,7 +120,7 @@ impl Cpus {
break; 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 let Some(max_cpus) = max_cpus {
if result.len() != max_cpus { if result.len() != max_cpus {
@ -127,6 +134,7 @@ impl Cpus {
cpus: result, cpus: result,
smt: true, smt: true,
smt_capable: can_smt, smt_capable: can_smt,
limits: oc_limits,
} }
} }
} }
@ -166,6 +174,7 @@ pub struct Cpu {
pub online: bool, pub online: bool,
pub clock_limits: Option<MinMax<u64>>, pub clock_limits: Option<MinMax<u64>>,
pub governor: String, pub governor: String,
limits: CpuLimits,
index: usize, index: usize,
state: crate::state::steam_deck::Cpu, 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 { impl Cpu {
#[inline] #[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 { match version {
0 => Self { 0 => Self {
online: other.online, online: other.online,
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)), clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
governor: other.governor, governor: other.governor,
limits: oc_limits,
index: i, index: i,
state: crate::state::steam_deck::Cpu::default(), state: crate::state::steam_deck::Cpu::default(),
}, },
@ -188,6 +198,7 @@ impl Cpu {
online: other.online, online: other.online,
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)), clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
governor: other.governor, governor: other.governor,
limits: oc_limits,
index: i, index: i,
state: crate::state::steam_deck::Cpu::default(), state: crate::state::steam_deck::Cpu::default(),
}, },
@ -250,7 +261,7 @@ impl Cpu {
// disable manual clock limits // disable manual clock limits
log::debug!("Setting CPU {} to default clockspeed", self.index); log::debug!("Setting CPU {} to default clockspeed", self.index);
// max clock // 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( usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|e| SettingError { |e| SettingError {
msg: format!( msg: format!(
@ -261,7 +272,7 @@ impl Cpu {
}, },
)?; )?;
// min clock // 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( usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|e| SettingError { |e| SettingError {
msg: format!( msg: format!(
@ -297,41 +308,33 @@ impl Cpu {
} }
fn clamp_all(&mut self) { fn clamp_all(&mut self) {
let min = Self::min();
let max = Self::max();
if let Some(clock_limits) = &mut self.clock_limits { if let Some(clock_limits) = &mut self.clock_limits {
let max_boost = max.clock_limits.as_ref().unwrap(); clock_limits.min = clock_limits.min.clamp(self.limits.clock_min.min, self.limits.clock_min.max);
let min_boost = min.clock_limits.as_ref().unwrap(); clock_limits.max = clock_limits.max.clamp(self.limits.clock_max.min, self.limits.clock_max.max);
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 { fn from_sys(cpu_index: usize, oc_limits: CpuLimits) -> Self {
Self { Self {
online: usdpl_back::api::files::read_single(cpu_online_path(cpu_index)).unwrap_or(1u8) != 0, online: usdpl_back::api::files::read_single(cpu_online_path(cpu_index)).unwrap_or(1u8) != 0,
clock_limits: None, clock_limits: None,
governor: usdpl_back::api::files::read_single(cpu_governor_path(cpu_index)) governor: usdpl_back::api::files::read_single(cpu_governor_path(cpu_index))
.unwrap_or("schedutil".to_owned()), .unwrap_or("schedutil".to_owned()),
limits: oc_limits,
index: cpu_index, index: cpu_index,
state: crate::state::steam_deck::Cpu::default(), state: crate::state::steam_deck::Cpu::default(),
} }
} }
fn limits(&self) -> crate::api::CpuLimits { 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 { crate::api::CpuLimits {
clock_min_limits: Some(RangeLimit { clock_min_limits: Some(RangeLimit {
min: min_clocks.min, min: self.limits.clock_min.min,
max: max_clocks.min max: self.limits.clock_min.max
}), }),
clock_max_limits: Some(RangeLimit { clock_max_limits: Some(RangeLimit {
min: min_clocks.max, min: self.limits.clock_max.min,
max: max_clocks.max max: self.limits.clock_max.max
}), }),
clock_step: 100, clock_step: 100,
governors: self.governors(), 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] #[inline]
fn cpu_online_path(index: usize) -> String { fn cpu_online_path(index: usize) -> String {
format!("/sys/devices/system/cpu/cpu{}/online", index) format!("/sys/devices/system/cpu/cpu{}/online", index)

View file

@ -2,9 +2,10 @@ use std::convert::Into;
use crate::api::RangeLimit; use crate::api::RangeLimit;
use crate::settings::MinMax; use crate::settings::MinMax;
use crate::settings::{OnResume, OnSet, SettingError, SettingsRange}; use crate::settings::{OnResume, OnSet, SettingError};
use crate::settings::TGpu; use crate::settings::TGpu;
use crate::persist::GpuJson; use crate::persist::GpuJson;
use super::oc_limits::{OverclockLimits, GpuLimits};
const SLOW_PPT: u8 = 1; const SLOW_PPT: u8 = 1;
const FAST_PPT: u8 = 2; const FAST_PPT: u8 = 2;
@ -15,6 +16,7 @@ pub struct Gpu {
pub slow_ppt: Option<u64>, pub slow_ppt: Option<u64>,
pub clock_limits: Option<MinMax<u64>>, pub clock_limits: Option<MinMax<u64>>,
pub slow_memory: bool, pub slow_memory: bool,
limits: GpuLimits,
state: crate::state::steam_deck::Gpu, 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 { impl Gpu {
#[inline] #[inline]
pub fn from_json(other: GpuJson, version: u64) -> Self { pub fn from_json(other: GpuJson, version: u64) -> Self {
let oc_limits = OverclockLimits::load_or_default().gpu;
match version { match version {
0 => Self { 0 => Self {
fast_ppt: other.fast_ppt, fast_ppt: other.fast_ppt,
slow_ppt: other.slow_ppt, slow_ppt: other.slow_ppt,
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)), clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
slow_memory: other.slow_memory, slow_memory: other.slow_memory,
limits: oc_limits,
state: crate::state::steam_deck::Gpu::default(), state: crate::state::steam_deck::Gpu::default(),
}, },
_ => Self { _ => Self {
@ -39,6 +43,7 @@ impl Gpu {
slow_ppt: other.slow_ppt, slow_ppt: other.slow_ppt,
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)), clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
slow_memory: other.slow_memory, slow_memory: other.slow_memory,
limits: oc_limits,
state: crate::state::steam_deck::Gpu::default(), state: crate::state::steam_deck::Gpu::default(),
}, },
} }
@ -120,7 +125,7 @@ impl Gpu {
self.state.clock_limits_set = false; self.state.clock_limits_set = false;
// disable manual clock limits // disable manual clock limits
// max clock // 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( usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|e| SettingError { |e| SettingError {
msg: format!( msg: format!(
@ -131,7 +136,7 @@ impl Gpu {
}, },
)?; )?;
// min clock // 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( usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|e| SettingError { |e| SettingError {
msg: format!( msg: format!(
@ -154,34 +159,32 @@ impl Gpu {
} }
fn clamp_all(&mut self) { fn clamp_all(&mut self) {
let min = Self::min();
let max = Self::max();
if let Some(fast_ppt) = &mut self.fast_ppt { if let Some(fast_ppt) = &mut self.fast_ppt {
*fast_ppt = (*fast_ppt).clamp( *fast_ppt = (*fast_ppt).clamp(
*min.fast_ppt.as_ref().unwrap(), self.limits.fast_ppt.min,
*max.fast_ppt.as_ref().unwrap(), self.limits.fast_ppt.max,
); );
} }
if let Some(slow_ppt) = &mut self.slow_ppt { if let Some(slow_ppt) = &mut self.slow_ppt {
*slow_ppt = (*slow_ppt).clamp( *slow_ppt = (*slow_ppt).clamp(
*min.slow_ppt.as_ref().unwrap(), self.limits.slow_ppt.min,
*max.slow_ppt.as_ref().unwrap(), self.limits.slow_ppt.max,
); );
} }
if let Some(clock_limits) = &mut self.clock_limits { if let Some(clock_limits) = &mut self.clock_limits {
let max_boost = max.clock_limits.as_ref().unwrap(); clock_limits.min = clock_limits.min.clamp(self.limits.clock_min.min, self.limits.clock_min.max);
let min_boost = min.clock_limits.as_ref().unwrap(); clock_limits.max = clock_limits.max.clamp(self.limits.clock_max.min, self.limits.clock_max.max);
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 { pub fn system_default() -> Self {
let oc_limits = OverclockLimits::load_or_default().gpu;
Self { Self {
fast_ppt: None, fast_ppt: None,
slow_ppt: None, slow_ppt: None,
clock_limits: None, clock_limits: None,
slow_memory: false, slow_memory: false,
limits: oc_limits,
state: crate::state::steam_deck::Gpu::default(), 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; const PPT_DIVISOR: u64 = 1_000_000;
impl TGpu for Gpu { impl TGpu for Gpu {
fn limits(&self) -> crate::api::GpuLimits { 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 { crate::api::GpuLimits {
fast_ppt_limits: Some(RangeLimit { fast_ppt_limits: Some(RangeLimit {
min: min.fast_ppt.unwrap() / PPT_DIVISOR, min: self.limits.fast_ppt.min / PPT_DIVISOR,
max: max.fast_ppt.unwrap() / PPT_DIVISOR, max: self.limits.fast_ppt.max / PPT_DIVISOR,
}), }),
slow_ppt_limits: Some(RangeLimit { slow_ppt_limits: Some(RangeLimit {
min: min.slow_ppt.unwrap() / PPT_DIVISOR, min: self.limits.slow_ppt.min / PPT_DIVISOR,
max: max.slow_ppt.unwrap() / PPT_DIVISOR, max: self.limits.slow_ppt.max / PPT_DIVISOR,
}), }),
ppt_step: 1, ppt_step: 1,
tdp_limits: None, tdp_limits: None,
tdp_boost_limits: None, tdp_boost_limits: None,
tdp_step: 42, tdp_step: 42,
clock_min_limits: Some(RangeLimit { clock_min_limits: Some(RangeLimit {
min: min_clock_limits.min, min: self.limits.clock_min.min,
max: max_clock_limits.max, max: self.limits.clock_min.max,
}), }),
clock_max_limits: Some(RangeLimit { clock_max_limits: Some(RangeLimit {
min: min_clock_limits.min, min: self.limits.clock_max.min,
max: max_clock_limits.max, max: self.limits.clock_max.max,
}), }),
clock_step: 100, clock_step: 100,
memory_control_capable: true, memory_control_capable: true,

View file

@ -1,6 +1,7 @@
mod battery; mod battery;
mod cpu; mod cpu;
mod gpu; mod gpu;
mod oc_limits;
mod util; mod util;
pub use battery::Battery; pub use battery::Battery;

View 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 }
}
}
}

View file

@ -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
)
}

View file

@ -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)
}

View file

@ -1,7 +0,0 @@
//mod battery;
mod cpu;
mod gpu;
//pub use battery::Battery;
pub use cpu::{Cpu, Cpus};
pub use gpu::Gpu;