Add Steam Deck max clock detection since OC can now be reported correctly

version 2.0.0-alpha3
This commit is contained in:
NGnius (Graham) 2024-02-10 12:52:21 -05:00
parent 1a4bfb9669
commit 3e3e9a68f4
9 changed files with 536 additions and 125 deletions

2
backend/Cargo.lock generated
View file

@ -1170,7 +1170,7 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "powertools"
version = "2.0.0-alpha2"
version = "2.0.0-alpha3"
dependencies = [
"async-trait",
"chrono",

View file

@ -1,6 +1,6 @@
[package]
name = "powertools"
version = "2.0.0-alpha2"
version = "2.0.0-alpha3"
edition = "2021"
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
description = "Backend (superuser) functionality for PowerTools"

View file

@ -77,16 +77,16 @@ impl Default for Base {
},
limits: super::Limits {
cpu: super::Limit {
provider: super::CpuLimitType::SteamDeck,
limits: super::GenericCpusLimit::default_for(super::CpuLimitType::SteamDeck),
provider: super::CpuLimitType::SteamDeckOLED,
limits: super::GenericCpusLimit::default_for(super::CpuLimitType::SteamDeckOLED),
},
gpu: super::Limit {
provider: super::GpuLimitType::SteamDeck,
provider: super::GpuLimitType::SteamDeckOLED,
limits: super::GenericGpuLimit::default_for(super::GpuLimitType::SteamDeckOLED),
},
battery: super::Limit {
provider: super::BatteryLimitType::SteamDeck,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::SteamDeck),
provider: super::BatteryLimitType::SteamDeckOLED,
limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::SteamDeckOLED),
},
}
},

View file

@ -93,13 +93,14 @@ pub struct GenericCpuLimit {
pub tdp_divisor: Option<u64>,
pub tdp_step: Option<u64>,
pub skip_resume_reclock: bool,
pub experiments: bool,
pub extras: super::LimitExtras,
}
impl GenericCpuLimit {
pub fn default_for(t: &CpuLimitType, _index: usize) -> Self {
match t {
CpuLimitType::SteamDeck | CpuLimitType::SteamDeckOLED => Self::default_steam_deck(),
CpuLimitType::SteamDeck => Self::default_steam_deck(),
CpuLimitType::SteamDeckOLED => Self::default_steam_deck_oled(),
CpuLimitType::DevMode => Self {
clock_min: Some(RangeLimit { min: Some(100), max: Some(5000) }),
clock_max: Some(RangeLimit { min: Some(100), max: Some(4800) }),
@ -109,7 +110,7 @@ impl GenericCpuLimit {
tdp_divisor: Some(1_000_000),
tdp_step: Some(1),
skip_resume_reclock: false,
experiments: true,
extras: Default::default(),
},
_ => Self {
clock_min: None,
@ -120,7 +121,7 @@ impl GenericCpuLimit {
tdp_divisor: None,
tdp_step: None,
skip_resume_reclock: false,
experiments: false,
extras: Default::default(),
},
}
}
@ -141,7 +142,32 @@ impl GenericCpuLimit {
tdp_divisor: None,
tdp_step: None,
skip_resume_reclock: false,
experiments: false,
extras: Default::default(),
}
}
fn default_steam_deck_oled() -> Self {
Self {
clock_min: Some(RangeLimit {
min: Some(1400),
max: Some(3500),
}),
clock_max: Some(RangeLimit {
min: Some(400),
max: Some(3500),
}),
clock_step: Some(100),
tdp: None,
tdp_boost: None,
tdp_divisor: None,
tdp_step: None,
skip_resume_reclock: false,
extras: super::LimitExtras {
experiments: false,
quirks: vec![
"clock-autodetect".to_owned(),
].into_iter().collect()
},
}
}
@ -165,6 +191,6 @@ impl GenericCpuLimit {
}
self.clock_step = limit_override.clock_step;
self.skip_resume_reclock = limit_override.skip_resume_reclock;
self.experiments = limit_override.experiments;
self.extras = limit_override.extras;
}
}

View file

@ -93,6 +93,7 @@ impl GenericGpuLimit {
fn default_steam_deck_oled() -> Self {
let mut sd = Self::default_steam_deck();
sd.memory_clock_step = Some(200);
sd.extras.quirks.insert("clock-autodetect".to_owned());
sd
}

File diff suppressed because it is too large Load diff

View file

@ -102,6 +102,9 @@ impl Cpus {
pub fn variant(mut self, model: super::Model) -> Self {
self.variant = model;
for cpu in self.cpus.iter_mut() {
cpu.variant(model)
}
self
}
}
@ -125,7 +128,12 @@ impl ProviderBuilder<Vec<CpuJson>, GenericCpusLimit> for Cpus {
}
}
let new_cpu = if let Some(cpu_limit) = limits.cpus.get(i) {
Cpu::from_json_and_limits(cpu, version, i, cpu_limit.to_owned())
let mut cpu_limit_clone = cpu_limit.to_owned();
for item in &limits.extras.quirks {
cpu_limit_clone.extras.quirks.insert(item.to_owned());
}
cpu_limit_clone.extras.experiments |= limits.extras.experiments;
Cpu::from_json_and_limits(cpu, version, i, cpu_limit_clone)
} else {
Cpu::from_json(cpu, version, i)
};
@ -262,6 +270,7 @@ pub struct Cpu {
index: usize,
state: crate::state::steam_deck::Cpu,
sysfs: BasicEntityPath,
variant: super::Model,
}
//const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
@ -289,6 +298,7 @@ impl Cpu {
index: i,
state: crate::state::steam_deck::Cpu::default(),
sysfs: Self::find_card_sysfs(other.root),
variant: super::Model::LCD,
},
_ => Self {
online: other.online,
@ -298,6 +308,7 @@ impl Cpu {
index: i,
state: crate::state::steam_deck::Cpu::default(),
sysfs: Self::find_card_sysfs(other.root),
variant: super::Model::LCD,
},
}
}
@ -341,6 +352,19 @@ impl Cpu {
}
}
fn read_max_cpu_clock(&self) -> u64 {
if !(self.limits.extras.experiments || self.limits.extras.quirks.contains("clock-autodetect")) {
return MAX_CLOCK;
}
if let super::Model::OLED = self.variant {
if let Ok(freq_khz) = usdpl_back::api::files::read_single::<_, u64, _>(cpu_max_clock_path(self.index)) {
log::debug!("Detected CPU max clock of {}KHz", freq_khz);
return freq_khz / 1000
}
}
MAX_CLOCK
}
fn set_clock_limit(
&self,
index: usize,
@ -591,6 +615,7 @@ impl Cpu {
index: cpu_index,
state: crate::state::steam_deck::Cpu::default(),
sysfs: Self::find_card_sysfs(None::<&'static str>),
variant: super::Model::LCD,
}
}
@ -602,14 +627,15 @@ impl Cpu {
}
fn limits(&self) -> crate::api::CpuLimits {
let max_cpu_clock = self.read_max_cpu_clock();
crate::api::CpuLimits {
clock_min_limits: Some(RangeLimit {
min: range_min_or_fallback(&self.limits.clock_max, MIN_MAX_CLOCK), // allows min to be set by max (it's weird, blame the kernel)
max: range_max_or_fallback(&self.limits.clock_min, MAX_CLOCK),
max: range_max_or_fallback(&self.limits.clock_min, max_cpu_clock),
}),
clock_max_limits: Some(RangeLimit {
min: range_min_or_fallback(&self.limits.clock_max, MIN_MAX_CLOCK),
max: range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK),
max: range_max_or_fallback(&self.limits.clock_max, max_cpu_clock),
}),
clock_step: self.limits.clock_step.unwrap_or(CLOCK_STEP),
governors: self.governors(),
@ -628,6 +654,10 @@ impl Cpu {
};
gov_str.split(' ').map(|s| s.to_owned()).collect()
}
pub fn variant(&mut self, model: super::Model) {
self.variant = model;
}
}
impl Into<CpuJson> for Cpu {
@ -714,3 +744,8 @@ fn cpu_available_governors_path(index: usize) -> String {
index
)
}
#[inline]
fn cpu_max_clock_path(index: usize) -> String {
format!("/sys/devices/system/cpu/cpufreq/policy{}/cpuinfo_max_freq", index)
}

View file

@ -37,6 +37,7 @@ pub struct Gpu {
const GPU_CLOCK_LIMITS_ATTRIBUTE: &str = "device/pp_od_clk_voltage";
const GPU_MEMORY_DOWNCLOCK_ATTRIBUTE: &str = "device/pp_dpm_fclk";
const GPU_CLOCK_READOUT_ATTRIBUTE: &str = "device/pp_dpm_sclk";
const CARD_EXTENSIONS: &[&'static str] = &[
GPU_CLOCK_LIMITS_ATTRIBUTE,
@ -141,6 +142,29 @@ impl Gpu {
})
}
fn read_max_gpu_clock(&self) -> u64 {
if !(self.limits.extras.experiments || self.limits.extras.quirks.contains("clock-autodetect")) {
return MAX_CLOCK;
}
if let super::Model::OLED = self.variant {
if let Ok(f) = self
.sysfs_card
.read_value(GPU_CLOCK_READOUT_ATTRIBUTE.to_owned())
{
let options = parse_pp_dpm_sclk(&String::from_utf8_lossy(&f));
return options.get(options.len() - 1)
.map(|x| {
let x = x.1 as u64;
log::debug!("Detected GPU max clock of {}MHz", x);
x
})
.unwrap_or(MAX_CLOCK);
}
}
MAX_CLOCK
}
fn is_memory_clock_maxed(&self) -> bool {
if let Some(clock) = &self.memory_clock {
if let Some(limit) = &self.limits.memory_clock {
@ -611,6 +635,7 @@ impl crate::settings::OnUnload for Gpu {
impl TGpu for Gpu {
fn limits(&self) -> crate::api::GpuLimits {
let max_gpu_clock = self.read_max_gpu_clock();
crate::api::GpuLimits {
fast_ppt_limits: Some(RangeLimit {
min: super::util::range_min_or_fallback(&self.limits.fast_ppt, MIN_FAST_PPT)
@ -630,11 +655,11 @@ impl TGpu for Gpu {
tdp_step: 42,
clock_min_limits: Some(RangeLimit {
min: super::util::range_min_or_fallback(&self.limits.clock_min, MIN_CLOCK),
max: super::util::range_max_or_fallback(&self.limits.clock_min, MAX_CLOCK),
max: super::util::range_max_or_fallback(&self.limits.clock_min, max_gpu_clock),
}),
clock_max_limits: Some(RangeLimit {
min: super::util::range_min_or_fallback(&self.limits.clock_max, MIN_CLOCK),
max: super::util::range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK),
max: super::util::range_max_or_fallback(&self.limits.clock_max, max_gpu_clock),
}),
clock_step: self.limits.clock_step.unwrap_or(100),
memory_control: Some(RangeLimit {
@ -693,7 +718,7 @@ impl TGpu for Gpu {
}
}
fn parse_pp_dpm_fclk(s: &str) -> Vec<(usize, usize)> {
fn parse_sysfs_clk_selector_str(s: &str) -> Vec<(usize, usize)> {
// (value, MHz)
let mut result = Vec::new();
for line in s.split('\n') {
@ -715,3 +740,13 @@ fn parse_pp_dpm_fclk(s: &str) -> Vec<(usize, usize)> {
}
result
}
#[inline]
fn parse_pp_dpm_fclk(s: &str) -> Vec<(usize, usize)> {
parse_sysfs_clk_selector_str(s)
}
#[inline]
fn parse_pp_dpm_sclk(s: &str) -> Vec<(usize, usize)> {
parse_sysfs_clk_selector_str(s)
}

View file

@ -1,6 +1,6 @@
{
"name": "PowerTools",
"version": "2.0.0-alpha2",
"version": "2.0.0-alpha3",
"description": "Power tweaks for power users",
"scripts": {
"build": "shx rm -rf dist && rollup -c",