diff --git a/src/manager/user.rs b/src/manager/user.rs index e61c634..49d572e 100644 --- a/src/manager/user.rs +++ b/src/manager/user.rs @@ -22,7 +22,7 @@ use crate::error::{to_zbus_error, to_zbus_fdo_error, zbus_to_zbus_fdo}; use crate::hardware::check_support; use crate::job::JobManagerCommand; use crate::power::{ - get_available_cpu_scaling_governors, get_cpu_scaling_governor, get_gpu_clocks, + get_available_cpu_scaling_governors, get_cpu_scaling_governor, get_gpu_clocks, get_gpu_clocks_range, get_gpu_performance_level, get_gpu_power_profile, get_gpu_power_profiles, get_tdp_limit, }; use crate::wifi::{get_wifi_backend, get_wifi_power_management_state}; @@ -309,15 +309,13 @@ impl SteamOSManager { } #[zbus(property(emits_changed_signal = "const"))] - async fn manual_gpu_clock_min(&self) -> u32 { - // TODO: Can this be queried from somewhere? - 200 + async fn manual_gpu_clock_min(&self) -> fdo::Result { + Ok(get_gpu_clocks_range().await.map_err(to_zbus_fdo_error)?.0) } #[zbus(property(emits_changed_signal = "const"))] - async fn manual_gpu_clock_max(&self) -> u32 { - // TODO: Can this be queried from somewhere? - 1600 + async fn manual_gpu_clock_max(&self) -> fdo::Result { + Ok(get_gpu_clocks_range().await.map_err(to_zbus_fdo_error)?.1) } #[zbus(property(emits_changed_signal = "false"))] diff --git a/src/power.rs b/src/power.rs index ba2872e..04df3bf 100644 --- a/src/power.rs +++ b/src/power.rs @@ -29,6 +29,7 @@ const CPU_POLICY_NAME: &str = "policy"; const GPU_POWER_PROFILE_SUFFIX: &str = "device/pp_power_profile_mode"; const GPU_PERFORMANCE_LEVEL_SUFFIX: &str = "device/power_dpm_force_performance_level"; const GPU_CLOCKS_SUFFIX: &str = "device/pp_od_clk_voltage"; +const GPU_CLOCK_LEVELS_SUFFIX: &str = "device/pp_dpm_sclk"; const CPU_SCALING_GOVERNOR_SUFFIX: &str = "scaling_governor"; const CPU_SCALING_AVAILABLE_GOVERNORS_SUFFIX: &str = "scaling_available_governors"; @@ -38,6 +39,8 @@ const TDP_LIMIT2: &str = "power2_cap"; lazy_static! { static ref GPU_POWER_PROFILE_REGEX: Regex = Regex::new(r"^\s*(?[0-9]+)\s+(?[0-9A-Za-z_]+)(?\*)?").unwrap(); + static ref GPU_CLOCK_LEVELS_REGEX: Regex = + Regex::new(r"^\s*(?[0-9]+): (?[0-9]+)Mhz").unwrap(); } #[derive(Display, EnumString, PartialEq, Debug, Copy, Clone)] @@ -274,6 +277,33 @@ pub(crate) async fn set_cpu_scaling_governor(governor: CPUScalingGovernor) -> Re write_cpu_governor_sysfs_contents(name).await } +pub(crate) async fn get_gpu_clocks_range() -> Result<(u32, u32)> { + let contents = read_gpu_sysfs_contents(GPU_CLOCK_LEVELS_SUFFIX).await?; + let lines = contents.lines(); + let mut min = 1000000; + let mut max = 0; + + for line in lines { + let caps = GPU_CLOCK_LEVELS_REGEX.captures(line); + let caps = match caps { + Some(caps) => caps, + None => continue, + }; + let value: u32 = caps["value"] + .parse() + .map_err(|message| anyhow!("Unable to parse value for GPU power profile: {message}"))?; + if value < min { + min = value; + } + if value > max { + max = value; + } + } + + ensure!(min <= max, "Could not read any clocks"); + Ok((min, max)) +} + pub(crate) async fn set_gpu_clocks(clocks: u32) -> Result<()> { // Set GPU clocks to given value valid // Only used when GPU Performance Level is manual, but write whenever called. @@ -633,6 +663,35 @@ CCLK_RANGE in Core0: assert_eq!(read_clocks().await.unwrap(), format_clocks(1600)); } + #[tokio::test] + async fn test_get_gpu_clocks_range() { + let _h = testing::start(); + + setup().await; + let base = find_hwmon().await.unwrap(); + let filename = base.join(GPU_CLOCK_LEVELS_SUFFIX); + create_dir_all(filename.parent().unwrap()) + .await + .expect("create_dir_all"); + + assert!(get_gpu_clocks_range().await.is_err()); + + write(filename.as_path(), &[] as &[u8; 0]).await.expect("write"); + assert!(get_gpu_clocks_range().await.is_err()); + + let contents = "0: 200Mhz * +1: 1100Mhz +2: 1600Mhz"; + write(filename.as_path(), contents).await.expect("write"); + assert_eq!(get_gpu_clocks_range().await.unwrap(), (200, 1600)); + + let contents = "0: 1600Mhz * +1: 200Mhz +2: 1100Mhz"; + write(filename.as_path(), contents).await.expect("write"); + assert_eq!(get_gpu_clocks_range().await.unwrap(), (200, 1600)); + } + #[test] fn gpu_power_profile_roundtrip() { enum_roundtrip!(GPUPowerProfile {