1use crate::AMDGPU::{
2 ASIC_NAME,
3 CHIP_CLASS,
4 DeviceHandle,
5 drm_amdgpu_info_device,
6 drm_amdgpu_memory_info,
7 FW_VERSION::{FW_TYPE, FwVer},
8 GPU_INFO,
9 HW_IP::{HW_IP_TYPE, HwIpInfo},
10 HwId,
11 HwmonTemp,
12 IpDieEntry,
13 PowerCap,
14 PowerProfile,
15 RasBlock,
16 RasErrorCount,
17 VBIOS::VbiosInfo,
18 VIDEO_CAPS::{CAP_TYPE, VideoCapsInfo},
19};
20use crate::{DevicePath, get_hw_ip_info_list, PCI, stat::Sensors};
21use std::path::PathBuf;
22
23#[derive(Debug, Clone)]
24pub struct AppDeviceInfo {
25 pub ext_info: drm_amdgpu_info_device,
26 pub memory_info: drm_amdgpu_memory_info,
27 pub is_apu: bool,
28 pub resizable_bar: bool,
29 pub min_dpm_link: Option<PCI::LINK>,
30 pub max_dpm_link: Option<PCI::LINK>,
31 pub max_gpu_link: Option<PCI::LINK>,
32 pub max_system_link: Option<PCI::LINK>,
33 pub min_gpu_clk: u32,
34 pub max_gpu_clk: u32,
35 pub max_od_gpu_clk: Option<u32>,
36 pub min_mem_clk: u32,
37 pub max_mem_clk: u32,
38 pub max_od_mem_clk: Option<u32>,
39 pub marketing_name: String,
40 pub asic_name: ASIC_NAME,
41 pub pci_bus: PCI::BUS_INFO,
42 pub sysfs_path: PathBuf,
43 pub edge_temp: Option<HwmonTemp>,
44 pub junction_temp: Option<HwmonTemp>,
45 pub memory_temp: Option<HwmonTemp>,
46 pub power_cap: Option<PowerCap>,
47 pub fan_max_rpm: Option<u32>,
48 pub decode: Option<VideoCapsInfo>,
49 pub encode: Option<VideoCapsInfo>,
50 pub vbios: Option<VbiosInfo>,
51 pub l1_cache_size_kib_per_cu: u32,
52 pub actual_num_tcc_blocks: u32,
53 pub gl1_cache_size_kib_per_sa: u32,
54 pub total_l2_cache_size_kib: u32,
55 pub total_l3_cache_size_mib: u32,
56 pub hw_ip_info_list: Vec<HwIpInfo>,
57 pub ip_die_entries: Vec<IpDieEntry>,
58 pub power_profiles: Vec<PowerProfile>,
59 pub gfx_target_version: Option<String>,
60 pub ecc_memory: bool,
61 pub has_npu: bool,
62 pub smc_fw_version: Option<u32>,
63 pub smu_ip_version: Option<(u8, u8, u8)>, pub fw_versions: Vec<FwVer>,
65 pub memory_vendor: Option<String>,
66 pub supports_gpu_metrics: bool,
67}
68
69impl AppDeviceInfo {
70 pub fn new(
71 amdgpu_dev: &DeviceHandle,
72 ext_info: &drm_amdgpu_info_device,
73 memory_info: &drm_amdgpu_memory_info,
74 sensors: &Option<Sensors>,
75 device_path: &DevicePath,
76 ) -> Self {
77 let (min_gpu_clk, max_gpu_clk) = amdgpu_dev.get_min_max_gpu_clock()
78 .unwrap_or_else(|| (0, (ext_info.max_engine_clock() / 1000) as u32));
79 let chip_class = ext_info.get_chip_class();
80 let (min_mem_clk, max_mem_clk) = amdgpu_dev.get_min_max_memory_clock()
81 .unwrap_or_else(|| (0, (ext_info.max_memory_clock() / 1000) as u32));
82 let resizable_bar = memory_info.check_resizable_bar();
83 let is_apu = ext_info.is_apu();
84 let marketing_name = device_path.device_name.clone();
85 let sysfs_path = device_path.sysfs_path.clone();
86 let hw_ip_info_list = get_hw_ip_info_list(amdgpu_dev, chip_class);
87 let ip_die_entries = IpDieEntry::get_all_entries_from_sysfs(&sysfs_path);
88 let power_profiles = PowerProfile::get_all_supported_profiles_from_sysfs(&sysfs_path);
89 let asic_name = ext_info.get_asic_name();
90 let gfx_target_version = ext_info.get_gfx_target_version().map(|v| v.to_string());
91
92 let max_od_gpu_clk;
93 let max_od_mem_clk;
94
95 {
96 let pp_od_clk_voltage = std::fs::read_to_string(sysfs_path.join("pp_od_clk_voltage"));
97
98 if is_apu {
99 max_od_gpu_clk = None;
100 max_od_mem_clk = None;
101 } else if chip_class == CHIP_CLASS::GFX12 && let Ok(s) = pp_od_clk_voltage {
102 max_od_gpu_clk = Self::parse_od_sclk_offset(&s)
106 .and_then(|[_min, max]| u32::try_from(max).ok())
107 .map(|v| v + max_gpu_clk);
108 max_od_mem_clk = Self::parse_od_mclk(&s)
109 .and_then(|[_min, max]| u32::try_from(max).ok());
110 } else if chip_class >= CHIP_CLASS::GFX10 && let Ok(s) = pp_od_clk_voltage {
111 max_od_gpu_clk = Self::parse_od_sclk(&s)
112 .and_then(|[_min, max]| u32::try_from(max).ok());
113 max_od_mem_clk = Self::parse_od_mclk(&s)
114 .and_then(|[_min, max]| u32::try_from(max).ok());
115 } else {
116 max_od_gpu_clk = None;
117 max_od_mem_clk = None;
118 }
119 }
120
121 let ecc_memory = RasErrorCount::get_from_sysfs_with_ras_block(&sysfs_path, RasBlock::UMC).is_ok();
122 let has_npu = is_apu && match asic_name {
123 ASIC_NAME::CHIP_GFX1103_R1 |
124 ASIC_NAME::CHIP_GFX1103_R1X => true,
125 _ => asic_name >= ASIC_NAME::CHIP_GFX1150,
126 };
127 let fw_versions = Self::get_fw_versions(amdgpu_dev);
128 let smc_fw_version = fw_versions
129 .iter()
130 .find(|fw_ver| fw_ver.fw_type == FW_TYPE::SMC)
131 .map(|fw_ver| fw_ver.version);
132 let smu_ip_version = ip_die_entries
133 .first()
134 .map(|entry| &entry.ip_hw_ids)
135 .and_then(|ip_hw_ids|
136 ip_hw_ids.iter().find(|ip| ip.hw_id == HwId::MP0 || ip.hw_id == HwId::MP1)
137 )
138 .and_then(|ip_hw_id| ip_hw_id.instances.first())
139 .map(|smu_ip| smu_ip.version());
140 let memory_vendor = std::fs::read_to_string(sysfs_path.join("mem_info_vram_vendor"))
141 .ok()
142 .map(|mut s| {
143 s.pop();
144 s
145 });
146 let supports_gpu_metrics = sysfs_path.join("gpu_metrics").exists();
147
148 Self {
149 ext_info: *ext_info,
150 memory_info: *memory_info,
151 resizable_bar,
152 is_apu,
153 min_dpm_link: sensors.as_ref().and_then(|s| s.min_dpm_link),
154 max_dpm_link: sensors.as_ref().and_then(|s| s.max_dpm_link),
155 max_gpu_link: sensors.as_ref().and_then(|s| s.max_gpu_link),
156 max_system_link: sensors.as_ref().and_then(|s| s.max_system_link),
157 min_gpu_clk,
158 max_gpu_clk,
159 max_od_gpu_clk,
160 min_mem_clk,
161 max_mem_clk,
162 max_od_mem_clk,
163 marketing_name,
164 asic_name,
165 pci_bus: device_path.pci,
166 sysfs_path,
167 edge_temp: sensors.as_ref().and_then(|s| s.edge_temp.clone()),
168 junction_temp: sensors.as_ref().and_then(|s| s.junction_temp.clone()),
169 memory_temp: sensors.as_ref().and_then(|s| s.memory_temp.clone()),
170 power_cap: sensors.as_ref().and_then(|s| s.power_cap.clone()),
171 fan_max_rpm: sensors.as_ref().and_then(|s| s.fan_max_rpm),
172 decode: amdgpu_dev.get_video_caps_info(CAP_TYPE::DECODE).ok(),
173 encode: amdgpu_dev.get_video_caps_info(CAP_TYPE::ENCODE).ok(),
174 vbios: amdgpu_dev.get_vbios_info().ok(),
175 actual_num_tcc_blocks: ext_info.get_actual_num_tcc_blocks(),
176 l1_cache_size_kib_per_cu: ext_info.get_l1_cache_size() >> 10,
177 gl1_cache_size_kib_per_sa: ext_info.get_gl1_cache_size() >> 10,
178 total_l2_cache_size_kib: ext_info.calc_l2_cache_size() >> 10,
179 total_l3_cache_size_mib: ext_info.calc_l3_cache_size_mb(),
180 hw_ip_info_list,
181 ip_die_entries,
182 power_profiles,
183 gfx_target_version,
184 ecc_memory,
185 has_npu,
186 smc_fw_version,
187 smu_ip_version,
188 fw_versions,
189 memory_vendor,
190 supports_gpu_metrics,
191 }
192 }
193
194 fn parse_mhz(s: &str) -> Option<i32> {
195 let len = s.len();
196 s.get(..len-3)?.parse::<i32>().ok()
197 }
198
199 fn parse_range(s: &str, start_str: &str) -> Option<[i32; 2]> {
200 let mut lines = s.lines();
201 let s_range = lines.find(|l| l.starts_with(start_str))?;
202 let mut split = s_range
203 .trim_start_matches(start_str)
204 .split_whitespace();
205
206 if let [Some(min), Some(max)] = [split.next(), split.next()]
207 .map(|v| v.and_then(Self::parse_mhz))
208 {
209 Some([min, max])
210 } else {
211 None
212 }
213 }
214
215 fn parse_od_sclk(s: &str) -> Option<[i32; 2]> {
216 Self::parse_range(s, "SCLK:")
217 }
218
219 fn parse_od_sclk_offset(s: &str) -> Option<[i32; 2]> {
220 Self::parse_range(s, "SCLK_OFFSET:")
221 }
222
223 fn parse_od_mclk(s: &str) -> Option<[i32; 2]> {
224 Self::parse_range(s, "MCLK:")
225 }
226
227 pub fn get_fw_versions(amdgpu_dev: &DeviceHandle) -> Vec<FwVer> {
228 const FW_LIST: &[FW_TYPE] = &[
229 FW_TYPE::VCE,
230 FW_TYPE::UVD,
231 FW_TYPE::GMC,
232 FW_TYPE::GFX_ME,
233 FW_TYPE::GFX_PFP,
234 FW_TYPE::GFX_CE,
235 FW_TYPE::GFX_RLC,
236 FW_TYPE::GFX_MEC,
237 FW_TYPE::SMC,
238 FW_TYPE::SDMA,
239 FW_TYPE::SOS,
240 FW_TYPE::ASD,
241 FW_TYPE::VCN,
242 FW_TYPE::GFX_RLC_RESTORE_LIST_CNTL,
243 FW_TYPE::GFX_RLC_RESTORE_LIST_GPM_MEM,
244 FW_TYPE::GFX_RLC_RESTORE_LIST_SRM_MEM,
245 FW_TYPE::DMCU,
246 FW_TYPE::TA,
247 FW_TYPE::DMCUB,
248 FW_TYPE::TOC,
249 ];
250
251 let mut fw_versions = Vec::with_capacity(24);
252
253 for fw_type in FW_LIST {
254 let fw_info = match amdgpu_dev.query_firmware_version(*fw_type, 0, 0) {
255 Ok(v) => v,
256 Err(_) => continue,
257 };
258 fw_versions.push(fw_info);
259 }
260
261 if let Ok(mec2) = amdgpu_dev.query_firmware_version(FW_TYPE::GFX_MEC, 0, 1) {
263 fw_versions.push(mec2);
264 }
265
266 fw_versions
267 }
268
269 pub fn menu_entry(&self) -> String {
270 format!("{} ({})", self.marketing_name, self.pci_bus)
271 }
272
273 pub fn has_vcn(&self) -> bool {
274 self.hw_ip_info_list.iter().any(|info| info.ip_type == HW_IP_TYPE::VCN_DEC)
275 }
276
277 pub fn has_vcn_unified(&self) -> bool {
278 let Some(vcn_enc) = self.hw_ip_info_list
279 .iter()
280 .find(|info| info.ip_type == HW_IP_TYPE::VCN_ENC) else { return false };
281
282 4 <= vcn_enc.info.hw_ip_version_major
283 }
284
285 pub fn has_vpe(&self) -> bool {
286 if self.hw_ip_info_list.iter().any(|info| info.ip_type == HW_IP_TYPE::VPE) {
287 return true;
288 }
289
290 let Some(ip_die) = self.ip_die_entries.first() else { return false };
291
292 ip_die.ip_hw_ids.iter().any(|i| i.hw_id == HwId::VPE)
293 }
294}