ohmu
·
2026-10-15
1#include "gpu_driver_specific.h"
2
3#include "adl.h"
4#include "common/library.h"
5#include "common/mallocHelper.h"
6#include "common/debug.h"
7
8// Helper function to convert ADL status code to string
9[[maybe_unused]] static const char* ffAdlStatusToString(int status) {
10 switch (status) {
11#define FF_ADL_STATUS_CASE(name) \
12 case name: \
13 return #name;
14 FF_ADL_STATUS_CASE(ADL_OK)
15 FF_ADL_STATUS_CASE(ADL_OK_WARNING)
16 FF_ADL_STATUS_CASE(ADL_OK_MODE_CHANGE)
17 FF_ADL_STATUS_CASE(ADL_OK_RESTART)
18 FF_ADL_STATUS_CASE(ADL_OK_WAIT)
19 FF_ADL_STATUS_CASE(ADL_ERR)
20 FF_ADL_STATUS_CASE(ADL_ERR_NOT_INIT)
21 FF_ADL_STATUS_CASE(ADL_ERR_INVALID_PARAM)
22 FF_ADL_STATUS_CASE(ADL_ERR_INVALID_PARAM_SIZE)
23 FF_ADL_STATUS_CASE(ADL_ERR_INVALID_ADL_IDX)
24 FF_ADL_STATUS_CASE(ADL_ERR_INVALID_CONTROLLER_IDX)
25 FF_ADL_STATUS_CASE(ADL_ERR_INVALID_DIPLAY_IDX)
26 FF_ADL_STATUS_CASE(ADL_ERR_NOT_SUPPORTED)
27 FF_ADL_STATUS_CASE(ADL_ERR_NULL_POINTER)
28 FF_ADL_STATUS_CASE(ADL_ERR_DISABLED_ADAPTER)
29 FF_ADL_STATUS_CASE(ADL_ERR_INVALID_CALLBACK)
30 FF_ADL_STATUS_CASE(ADL_ERR_RESOURCE_CONFLICT)
31 FF_ADL_STATUS_CASE(ADL_ERR_SET_INCOMPLETE)
32 FF_ADL_STATUS_CASE(ADL_ERR_NO_XDISPLAY)
33 FF_ADL_STATUS_CASE(ADL_ERR_CALL_TO_INCOMPATIABLE_DRIVER)
34 FF_ADL_STATUS_CASE(ADL_ERR_NO_ADMINISTRATOR_PRIVILEGES)
35 FF_ADL_STATUS_CASE(ADL_ERR_FEATURESYNC_NOT_STARTED)
36 FF_ADL_STATUS_CASE(ADL_ERR_INVALID_POWER_STATE)
37#undef FF_ADL_STATUS_CASE
38 default:
39 return "Unknown ADL error";
40 }
41}
42
43// Memory allocation function
44static void* __stdcall ffAdlMainMemoryAlloc(int iSize) {
45 return malloc((size_t) iSize);
46}
47
48struct FFAdlData {
49 FF_LIBRARY_SYMBOL(ADL2_Main_Control_Destroy)
50 FF_LIBRARY_SYMBOL(ADL2_Adapter_AdapterInfoX3_Get)
51 FF_LIBRARY_SYMBOL(ADL2_Adapter_Graphic_Core_Info_Get)
52 FF_LIBRARY_SYMBOL(ADL2_Adapter_MemoryInfo2_Get)
53 FF_LIBRARY_SYMBOL(ADL2_Adapter_DedicatedVRAMUsage_Get)
54 FF_LIBRARY_SYMBOL(ADL2_Adapter_ASICFamilyType_Get)
55 FF_LIBRARY_SYMBOL(ADL2_Adapter_ChipSetInfo_Get)
56 FF_LIBRARY_SYMBOL(ADL2_Overdrive_Caps)
57 FF_LIBRARY_SYMBOL(ADL2_OverdriveN_CapabilitiesX2_Get)
58 FF_LIBRARY_SYMBOL(ADL2_OverdriveN_SystemClocksX2_Get)
59 FF_LIBRARY_SYMBOL(ADL2_OverdriveN_PerformanceStatus_Get)
60 FF_LIBRARY_SYMBOL(ADL2_OverdriveN_Temperature_Get)
61 FF_LIBRARY_SYMBOL(ADL2_Overdrive8_Current_Setting_Get)
62 FF_LIBRARY_SYMBOL(ADL2_New_QueryPMLogData_Get)
63 FF_LIBRARY_SYMBOL(ADL2_Overdrive6_CurrentStatus_Get)
64 FF_LIBRARY_SYMBOL(ADL2_Overdrive6_Temperature_Get)
65 FF_LIBRARY_SYMBOL(ADL2_Overdrive6_StateInfo_Get)
66
67 bool inited;
68 ADL_CONTEXT_HANDLE apiHandle;
69} adlData;
70
71static void shutdownAdl() {
72 if (adlData.apiHandle) {
73 FF_DEBUG("Destroying ADL context");
74 adlData.ffADL2_Main_Control_Destroy(adlData.apiHandle);
75 adlData.apiHandle = nullptr;
76 }
77}
78
79const char* ffDetectAmdGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName) {
80 FF_DEBUG("Attempting to detect AMD GPU info using '%s'", soName);
81
82 if (!adlData.inited) {
83 adlData.inited = true;
84 FF_DEBUG("Initializing ADL library");
85 FF_LIBRARY_LOAD(atiadl, "dlopen atiadlxx failed", soName, 1);
86 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(atiadl, ADL2_Main_Control_Create)
87 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Main_Control_Destroy)
88 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Adapter_AdapterInfoX3_Get)
89 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Adapter_Graphic_Core_Info_Get)
90 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Adapter_MemoryInfo2_Get)
91 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Adapter_DedicatedVRAMUsage_Get)
92 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Adapter_ASICFamilyType_Get)
93 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Adapter_ChipSetInfo_Get)
94 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Overdrive_Caps)
95 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_OverdriveN_CapabilitiesX2_Get)
96 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_OverdriveN_SystemClocksX2_Get)
97 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_OverdriveN_PerformanceStatus_Get)
98 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Overdrive8_Current_Setting_Get)
99 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_New_QueryPMLogData_Get)
100 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_OverdriveN_Temperature_Get)
101 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Overdrive6_CurrentStatus_Get)
102 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Overdrive6_Temperature_Get)
103 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(atiadl, adlData, ADL2_Overdrive6_StateInfo_Get)
104 FF_DEBUG("ADL library loaded");
105
106 int result = ffADL2_Main_Control_Create(ffAdlMainMemoryAlloc, 1 /*iEnumConnectedAdapters*/, &adlData.apiHandle);
107 FF_DEBUG("ADL2_Main_Control_Create returned %s (%d)", ffAdlStatusToString(result), result);
108 if (result != ADL_OK) {
109 return "ffADL2_Main_Control_Create() failed";
110 }
111
112 atexit(shutdownAdl);
113 atiadl = nullptr; // don't close atiadl
114 FF_DEBUG("ADL initialization complete");
115 }
116
117 if (!adlData.apiHandle) {
118 FF_DEBUG("ADL context not initialized");
119 return "ffADL2_Main_Control_Create() failed";
120 }
121
122 FF_AUTO_FREE AdapterInfo* devices = nullptr;
123 int numDevices = 0;
124 int adapterResult = adlData.ffADL2_Adapter_AdapterInfoX3_Get(adlData.apiHandle, -1, &numDevices, &devices);
125 FF_DEBUG("ADL2_Adapter_AdapterInfoX3_Get returned %s (%d)", ffAdlStatusToString(adapterResult), adapterResult);
126
127 if (adapterResult == ADL_OK) {
128 FF_DEBUG("found %d adapters", numDevices);
129 } else {
130 FF_DEBUG("ffADL2_Adapter_AdapterInfoX3_Get() failed");
131 return "ffADL2_Adapter_AdapterInfoX3_Get() failed";
132 }
133
134 const AdapterInfo* device = nullptr;
135 for (int iDev = 0; iDev < numDevices; iDev++) {
136 if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID) {
137 FF_DEBUG("Checking device %d: bus=%d, device=%d, func=%d against requested bus=%u, device=%u, func=%u",
138 iDev,
139 devices[iDev].iBusNumber,
140 devices[iDev].iDeviceNumber,
141 devices[iDev].iFunctionNumber,
142 cond->pciBusId.bus,
143 cond->pciBusId.device,
144 cond->pciBusId.func);
145
146 if (
147 cond->pciBusId.bus == (uint32_t) devices[iDev].iBusNumber &&
148 cond->pciBusId.device == (uint32_t) devices[iDev].iDeviceNumber &&
149 cond->pciBusId.func == (uint32_t) devices[iDev].iFunctionNumber) {
150 device = &devices[iDev];
151 FF_DEBUG("Found matching device: %s (index: %d)", device->strAdapterName, device->iAdapterIndex);
152 break;
153 }
154 }
155 }
156
157 if (!device) {
158 FF_DEBUG("Device not found");
159 return "Device not found";
160 }
161
162 if (result.coreCount) {
163 ADLGraphicCoreInfo coreInfo;
164 int status = adlData.ffADL2_Adapter_Graphic_Core_Info_Get(adlData.apiHandle, device->iAdapterIndex, &coreInfo);
165 FF_DEBUG("ADL2_Adapter_Graphic_Core_Info_Get returned %s (%d)", ffAdlStatusToString(status), status);
166
167 if (status == ADL_OK) {
168 FF_DEBUG("Core info - NumCUs: %d, NumPEsPerCU: %d", coreInfo.iNumCUs, coreInfo.iNumPEsPerCU);
169 *result.coreCount = (uint32_t) coreInfo.iNumCUs * (uint32_t) coreInfo.iNumPEsPerCU;
170 FF_DEBUG("Got core count: %u", *result.coreCount);
171 } else {
172 FF_DEBUG("Failed to get core count");
173 }
174 }
175
176 if (result.memory) {
177 int vramUsage = 0;
178 int status = adlData.ffADL2_Adapter_DedicatedVRAMUsage_Get(adlData.apiHandle, device->iAdapterIndex, &vramUsage);
179 FF_DEBUG("ADL2_Adapter_DedicatedVRAMUsage_Get returned %s (%d), usage: %d MB",
180 ffAdlStatusToString(status),
181 status,
182 vramUsage);
183
184 if (status == ADL_OK && vramUsage >= 0) {
185 result.memory->used = (uint64_t) vramUsage * 1024 * 1024;
186 FF_DEBUG("Dedicated VRAM usage: %llu bytes (%d MB)", result.memory->used, vramUsage);
187 } else {
188 FF_DEBUG("Failed to get dedicated VRAM usage");
189 }
190 }
191
192 if (result.memoryType) {
193 ADLMemoryInfo2 memoryInfo;
194 int status = adlData.ffADL2_Adapter_MemoryInfo2_Get(adlData.apiHandle, device->iAdapterIndex, &memoryInfo);
195 FF_DEBUG("ADL2_Adapter_MemoryInfo2_Get returned %s (%d)", ffAdlStatusToString(status), status);
196
197 if (status == ADL_OK) {
198 FF_DEBUG("Memory info - Type: %s, Size: %lld MB", memoryInfo.strMemoryType, memoryInfo.iMemorySize / 1024 / 1024);
199 ffStrbufSetS(result.memoryType, memoryInfo.strMemoryType);
200 FF_DEBUG("Got memory type: %s", memoryInfo.strMemoryType);
201 } else {
202 FF_DEBUG("Failed to get memory type");
203 }
204 }
205
206 if (result.type) {
207 int asicTypes = 0;
208 int valids = 0;
209 int status = adlData.ffADL2_Adapter_ASICFamilyType_Get(adlData.apiHandle, device->iAdapterIndex, &asicTypes, &valids);
210 FF_DEBUG("ADL2_Adapter_ASICFamilyType_Get returned %s (%d), asicTypes: 0x%x, valids: 0x%x",
211 ffAdlStatusToString(status),
212 status,
213 asicTypes,
214 valids);
215
216 if (status == ADL_OK) {
217 asicTypes &= valids; // This design is strange
218 *result.type = asicTypes & ADL_ASIC_INTEGRATED ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
219 FF_DEBUG("GPU type: %s (asicTypes: 0x%x, valids: 0x%x)",
220 *result.type == FF_GPU_TYPE_INTEGRATED ? "Integrated" : "Discrete",
221 asicTypes,
222 valids);
223 } else {
224 FF_DEBUG("Failed to get GPU type");
225 }
226 }
227
228 if (result.index) {
229 *result.index = (uint32_t) device->iAdapterIndex;
230 FF_DEBUG("Setting adapter index: %u", *result.index);
231 }
232
233 if (result.name) {
234 ffStrbufSetS(result.name, device->strAdapterName);
235 FF_DEBUG("Setting adapter name: %s; UDID: %s, Present: %d, Exist: %d", device->strAdapterName, device->strUDID, device->iPresent, device->iExist);
236 }
237
238 if (result.psMax || result.psCurr) {
239 ADLChipSetInfo chipSetInfo;
240 int status = adlData.ffADL2_Adapter_ChipSetInfo_Get(adlData.apiHandle, device->iAdapterIndex, &chipSetInfo);
241 FF_DEBUG("ADL2_Adapter_ChipSetInfo_Get returned %s (%d)", ffAdlStatusToString(status), status);
242
243 if (status == ADL_OK) {
244 FF_DEBUG("Chipset info - iBusSpeedType: %d, iMaxPCIELaneWidth: %d", chipSetInfo.iBusSpeedType, chipSetInfo.iMaxPCIELaneWidth);
245 if (result.psMax) {
246 if (chipSetInfo.iBusSpeedType >= 2) {
247 result.psMax->gen = (uint16_t) (chipSetInfo.iBusSpeedType - 2);
248 FF_DEBUG("Got PCIe Max Gen: %u", result.psMax->gen);
249 }
250 if (chipSetInfo.iMaxPCIELaneWidth > 0) {
251 result.psMax->lanes = (uint16_t) chipSetInfo.iMaxPCIELaneWidth;
252 FF_DEBUG("Got PCIe Max Lanes: %u", result.psMax->lanes);
253 }
254 }
255
256 FF_DEBUG("Chipset info - iBusType: %d, iCurrentPCIELaneWidth: %d", chipSetInfo.iBusType, chipSetInfo.iCurrentPCIELaneWidth);
257 if (result.psCurr) {
258 if (chipSetInfo.iBusType >= 2) {
259 result.psCurr->gen = (uint16_t) (chipSetInfo.iBusType - 2);
260 FF_DEBUG("Got PCIe Current Gen: %u", result.psCurr->gen);
261 }
262 if (chipSetInfo.iCurrentPCIELaneWidth > 0) {
263 result.psCurr->lanes = (uint16_t) chipSetInfo.iCurrentPCIELaneWidth;
264 FF_DEBUG("Got PCIe Current Lanes: %u", result.psCurr->lanes);
265 }
266 }
267 } else {
268 FF_DEBUG("Failed to get chipset information");
269 }
270 }
271
272 int odVersion = 0;
273
274 {
275 int odSupported = 0;
276 int odEnabled = 0;
277 int status = adlData.ffADL2_Overdrive_Caps(adlData.apiHandle, device->iAdapterIndex, &odSupported, &odEnabled, &odVersion);
278 FF_DEBUG("ADL2_Overdrive_Caps returned %s (%d); supported %d, enabled %d; version %d",
279 ffAdlStatusToString(status),
280 status,
281 odSupported,
282 odEnabled,
283 odVersion);
284 if (status != ADL_OK) {
285 FF_DEBUG("Overdrive not supported, results may be inaccurate");
286 // Note even if Overdrive is not supported, we can still get the OD version
287 }
288 }
289
290 if (odVersion == 8) {
291 FF_DEBUG("Using Overdrive8 API (odVersion=%d)", odVersion);
292
293 if (result.frequency) {
294 ADLOD8CurrentSetting currentSetting = { .count = OD8_COUNT };
295 int status = adlData.ffADL2_Overdrive8_Current_Setting_Get(adlData.apiHandle, device->iAdapterIndex, ¤tSetting);
296 FF_DEBUG("ADL2_Overdrive8_Current_Setting_Get returned %s (%d)", ffAdlStatusToString(status), status);
297 if (status == ADL_OK) {
298 FF_DEBUG("OD8 Settings count: %d", currentSetting.count);
299
300 *result.frequency = (uint32_t) currentSetting.Od8SettingTable[OD8_GFXCLK_FMAX];
301 FF_DEBUG("Got max engine clock (OD8_GFXCLK_FMAX): %u MHz", *result.frequency);
302 } else {
303 FF_DEBUG("Failed to get max frequency information");
304 }
305 }
306
307 if (result.temp || result.coreUsage) {
308 ADLPMLogDataOutput pmLogDataOutput = {};
309 int status = adlData.ffADL2_New_QueryPMLogData_Get(adlData.apiHandle, device->iAdapterIndex, &pmLogDataOutput);
310 FF_DEBUG("ADL2_New_QueryPMLogData_Get returned %s (%d)", ffAdlStatusToString(status), status);
311 if (status == ADL_OK) {
312 if (result.temp) {
313 ADLSingleSensorData* sensor = &pmLogDataOutput.sensors[ADL_PMLOG_TEMPERATURE_HOTSPOT];
314 FF_DEBUG("Sensor %d: %s, supported: %d, value: %d", ADL_PMLOG_TEMPERATURE_HOTSPOT, "ADL_PMLOG_TEMPERATURE_HOTSPOT", sensor->supported, sensor->value);
315 if (sensor->supported) {
316 *result.temp = sensor->value;
317 FF_DEBUG("Temperature: %.1f°C (HOTSPOT)", *result.temp);
318 } else {
319 sensor = &pmLogDataOutput.sensors[ADL_PMLOG_TEMPERATURE_GFX];
320 FF_DEBUG("Sensor %d: %s, supported: %d, value: %d", ADL_PMLOG_TEMPERATURE_GFX, "ADL_PMLOG_TEMPERATURE_GFX", sensor->supported, sensor->value);
321 if (sensor->supported) {
322 *result.temp = sensor->value;
323 FF_DEBUG("Temperature: %.1f°C (GFX)", *result.temp);
324 } else {
325 sensor = &pmLogDataOutput.sensors[ADL_PMLOG_TEMPERATURE_SOC];
326 FF_DEBUG("Sensor %d: %s, supported: %d, value: %d", ADL_PMLOG_TEMPERATURE_SOC, "ADL_PMLOG_TEMPERATURE_SOC", sensor->supported, sensor->value);
327 if (sensor->supported) {
328 *result.temp = sensor->value;
329 FF_DEBUG("Temperature: %.1f°C (SOC)", *result.temp);
330 } else {
331 FF_DEBUG("No supported temp sensor found, temp detection failed");
332 }
333 }
334 }
335 }
336 if (result.coreUsage) {
337 ADLSingleSensorData* activity = &pmLogDataOutput.sensors[ADL_PMLOG_INFO_ACTIVITY_GFX];
338 FF_DEBUG("Sensor %d: %s, supported: %d, value: %d", ADL_PMLOG_INFO_ACTIVITY_GFX, "ADL_PMLOG_INFO_ACTIVITY_GFX", activity->supported, activity->value);
339 if (activity->supported) {
340 *result.coreUsage = activity->value;
341 FF_DEBUG("Core usage: %.1f%%", *result.coreUsage);
342 } else {
343 FF_DEBUG("Sensor %d not supported, GPU usage detection failed", ADL_PMLOG_INFO_ACTIVITY_GFX);
344 }
345 }
346 } else {
347 FF_DEBUG("Failed to get temperature / GPU activity");
348 }
349 }
350 } else if (odVersion == 7) {
351 FF_DEBUG("Using OverdriveN API (odVersion=%d)", odVersion);
352
353 if (result.frequency) {
354 // https://github.com/MaynardMiner/odvii/blob/master/OverdriveN.cpp#L176
355 ADLODNCapabilitiesX2 odCapabilities = {};
356 int status = adlData.ffADL2_OverdriveN_CapabilitiesX2_Get(adlData.apiHandle, device->iAdapterIndex, &odCapabilities);
357 FF_DEBUG("ADL2_OverdriveN_CapabilitiesX2_Get returned %s (%d)", ffAdlStatusToString(status), status);
358
359 if (status == ADL_OK) {
360 if (odCapabilities.iMaximumNumberOfPerformanceLevels == 0) {
361 FF_DEBUG("ADL2_OverdriveN_CapabilitiesX2_Get: no performance levels available");
362 } else {
363 FF_DEBUG("ODN Capabilities - MaxPerformanceLevels: %d, GPU Clock Range: [%d - %d]",
364 odCapabilities.iMaximumNumberOfPerformanceLevels,
365 odCapabilities.sEngineClockRange.iMin,
366 odCapabilities.sEngineClockRange.iMax);
367
368 size_t size = sizeof(ADLODNPerformanceLevelsX2) + sizeof(ADLODNPerformanceLevelX2) * ((unsigned) odCapabilities.iMaximumNumberOfPerformanceLevels - 1);
369 FF_AUTO_FREE ADLODNPerformanceLevelsX2* odPerfLevels = calloc(size, 1);
370 odPerfLevels->iSize = (int) size;
371 odPerfLevels->iNumberOfPerformanceLevels = odCapabilities.iMaximumNumberOfPerformanceLevels;
372 odPerfLevels->iMode = ODNControlType_Current;
373
374 int status = adlData.ffADL2_OverdriveN_SystemClocksX2_Get(adlData.apiHandle, device->iAdapterIndex, odPerfLevels);
375 FF_DEBUG("ADL2_OverdriveN_SystemClocksX2_Get returned %s (%d), levels: %d",
376 ffAdlStatusToString(status),
377 status,
378 odPerfLevels->iNumberOfPerformanceLevels);
379
380 if (status != ADL_OK) {
381 FF_DEBUG("Failed to get frequency information");
382 } else {
383 // lowest to highest
384 for (int i = odPerfLevels->iNumberOfPerformanceLevels - 1; i >= 0; i--) {
385 ADLODNPerformanceLevelX2* level = &odPerfLevels->aLevels[i];
386 FF_DEBUG("Performance level %d: enabled: %d, engine clock = %d", i, level->iEnabled, level->iClock);
387 if (level->iEnabled) {
388 *result.frequency = (uint32_t) level->iClock / 100; // in 10 kHz
389 FF_DEBUG("Got max engine clock: %u MHz", *result.frequency);
390 break;
391 }
392 }
393 }
394 }
395 } else {
396 FF_DEBUG("Failed to get frequency information");
397 }
398 }
399
400 if (result.coreUsage) {
401 ADLODNPerformanceStatus performanceStatus = {};
402 int status = adlData.ffADL2_OverdriveN_PerformanceStatus_Get(adlData.apiHandle, device->iAdapterIndex, &performanceStatus);
403 FF_DEBUG("ADL2_OverdriveN_PerformanceStatus_Get returned %s (%d)", ffAdlStatusToString(status), status);
404
405 if (status == ADL_OK) {
406 FF_DEBUG("Performance Status - Activity: %d%%, CoreClock: %dMHz, MemoryClock: %dMHz",
407 performanceStatus.iGPUActivityPercent,
408 performanceStatus.iCoreClock,
409 performanceStatus.iMemoryClock);
410
411 *result.coreUsage = performanceStatus.iGPUActivityPercent;
412 FF_DEBUG("Got GPU activity: %d%%", performanceStatus.iGPUActivityPercent);
413 } else {
414 FF_DEBUG("Failed to get GPU activity");
415 }
416 }
417
418 if (result.temp) {
419 int milliDegrees = 0;
420 int status = adlData.ffADL2_OverdriveN_Temperature_Get(adlData.apiHandle, device->iAdapterIndex, 1, &milliDegrees);
421 FF_DEBUG("ADL2_OverdriveN_Temperature_Get returned %s (%d)", ffAdlStatusToString(status), status);
422
423 if (status == ADL_OK) {
424 *result.temp = milliDegrees / 1000.0;
425 FF_DEBUG("Temperature: %.1f°C (raw: %d milliC)", *result.temp, milliDegrees);
426 } else {
427 FF_DEBUG("Failed to get temperature");
428 }
429 }
430 } else if (odVersion == 6) {
431 FF_DEBUG("Using Overdrive6 API (odVersion=%d)", odVersion);
432
433 if (result.frequency) {
434 FF_AUTO_FREE ADLOD6StateInfo* stateInfo = calloc(sizeof(ADLOD6StateInfo) + sizeof(ADLOD6PerformanceLevel), 1);
435 stateInfo->iNumberOfPerformanceLevels = 2;
436
437 int status = adlData.ffADL2_Overdrive6_StateInfo_Get(adlData.apiHandle, device->iAdapterIndex, ADL_OD6_GETSTATEINFO_CUSTOM_PERFORMANCE, stateInfo);
438 FF_DEBUG("ADL2_Overdrive6_StateInfo_Get returned %s (%d), performance levels: %d",
439 ffAdlStatusToString(status),
440 status,
441 stateInfo->iNumberOfPerformanceLevels);
442
443 if (status == ADL_OK) {
444 // OD6 uses clock ranges instead of discrete performance levels.
445 // iNumberOfPerformanceLevels is always 2.
446 // The 1st level indicates the minimum clocks in the range.
447 // The 2nd level indicates the maximum clocks in the range.
448 if (stateInfo->iNumberOfPerformanceLevels != 2) {
449 FF_DEBUG("ADL2_Overdrive6_StateInfo_Get: unexpected number of performance levels: %d", stateInfo->iNumberOfPerformanceLevels);
450 } else {
451 FF_DEBUG("OD6 Settings - MinPerformanceLevels: %d, MaxPerformanceLevels: %d",
452 stateInfo->aLevels[0].iEngineClock,
453 stateInfo->aLevels[1].iEngineClock);
454 *result.frequency = (uint32_t) stateInfo->aLevels[1].iEngineClock / 100; // in 10 kHz
455 FF_DEBUG("Got max engine clock: %u MHz", *result.frequency);
456 }
457 } else {
458 FF_DEBUG("Failed to get frequency information");
459 }
460 }
461
462 if (result.coreUsage) {
463 ADLOD6CurrentStatus status = {};
464 int apiStatus = adlData.ffADL2_Overdrive6_CurrentStatus_Get(adlData.apiHandle, device->iAdapterIndex, &status);
465 FF_DEBUG("ADL2_Overdrive6_CurrentStatus_Get returned %s (%d)", ffAdlStatusToString(apiStatus), apiStatus);
466
467 if (apiStatus == ADL_OK) {
468 *result.coreUsage = status.iActivityPercent;
469 FF_DEBUG("Got GPU activity: %d%%", status.iActivityPercent);
470 } else {
471 FF_DEBUG("Failed to get GPU activity");
472 }
473 }
474
475 if (result.temp) {
476 int milliDegrees = 0;
477 int status = adlData.ffADL2_Overdrive6_Temperature_Get(adlData.apiHandle, device->iAdapterIndex, &milliDegrees);
478 FF_DEBUG("ADL2_Overdrive6_Temperature_Get returned %s (%d), temperature: %d milliC",
479 ffAdlStatusToString(status),
480 status,
481 milliDegrees);
482
483 if (status == ADL_OK) {
484 *result.temp = milliDegrees / 1000.0;
485 FF_DEBUG("Temperature: %.1f°C", *result.temp);
486 } else {
487 FF_DEBUG("Failed to get temperature");
488 }
489 }
490 } else {
491 FF_DEBUG("Unknown Overdrive version: %d", odVersion);
492 return "Unknown Overdrive version";
493 }
494 FF_DEBUG("AMD GPU detection complete - returning success");
495 return nullptr;
496}