sysclk: finish display refresh rate change support

This commit is contained in:
souldbminersmwc
2025-12-24 17:27:41 -05:00
parent 15fc7bc8f2
commit b335412bc4
12 changed files with 490 additions and 101 deletions

View File

@@ -77,7 +77,7 @@ typedef enum
SysClkModule_GPU, SysClkModule_GPU,
SysClkModule_MEM, SysClkModule_MEM,
HorizonOCModule_Governor, HorizonOCModule_Governor,
HorizonOCModule_LCD, HorizonOCModule_Display,
SysClkModule_EnumMax, SysClkModule_EnumMax,
} SysClkModule; } SysClkModule;
@@ -131,6 +131,8 @@ static inline const char* sysclkFormatModule(SysClkModule module, bool pretty)
return pretty ? "GPU" : "gpu"; return pretty ? "GPU" : "gpu";
case SysClkModule_MEM: case SysClkModule_MEM:
return pretty ? "Memory" : "mem"; return pretty ? "Memory" : "mem";
case HorizonOCModule_Display:
return pretty ? "Display" : "display";
case HorizonOCModule_Governor: case HorizonOCModule_Governor:
return pretty ? "Governor" : "gov"; return pretty ? "Governor" : "gov";
default: default:
@@ -148,6 +150,11 @@ static inline const char* sysclkFormatThermalSensor(SysClkThermalSensor thermSen
return pretty ? "PCB" : "pcb"; return pretty ? "PCB" : "pcb";
case SysClkThermalSensor_Skin: case SysClkThermalSensor_Skin:
return pretty ? "Skin" : "skin"; return pretty ? "Skin" : "skin";
case HorizonOCThermalSensor_Battery:
return pretty ? "BAT" : "battery";
case HorizonOCThermalSensor_PMIC:
return pretty ? "PMIC" : "pmic";
default: default:
return NULL; return NULL;
} }

View File

@@ -28,7 +28,8 @@
#pragma once #pragma once
#include <cstdio> #include <cstdio>
#include <string>
#include <cstdint>
#define FREQ_DEFAULT_TEXT "Default" #define FREQ_DEFAULT_TEXT "Default"
static inline std::string formatListFreqMHz(std::uint32_t mhz) static inline std::string formatListFreqMHz(std::uint32_t mhz)

View File

@@ -67,6 +67,32 @@ void AppProfileGui::openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfil
); );
} }
void AppProfileGui::openValueChoiceGui(
tsl::elm::ListItem* listItem,
std::uint32_t currentValue,
const ValueRange& range,
const std::string& categoryName,
ValueChoiceListener listener,
const ValueThresholds& thresholds,
bool enableThresholds,
const std::map<std::uint32_t, std::string>& labels,
const std::vector<NamedValue>& namedValues,
bool showDefaultValue
)
{
tsl::changeTo<ValueChoiceGui>(
currentValue,
range,
categoryName,
listener,
thresholds,
enableThresholds,
labels,
namedValues,
showDefaultValue
);
}
void AppProfileGui::addModuleListItem(SysClkProfile profile, SysClkModule module) void AppProfileGui::addModuleListItem(SysClkProfile profile, SysClkModule module)
{ {
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(sysclkFormatModule(module, true)); tsl::elm::ListItem* listItem = new tsl::elm::ListItem(sysclkFormatModule(module, true));
@@ -83,7 +109,6 @@ void AppProfileGui::addModuleListItem(SysClkProfile profile, SysClkModule module
this->profileList->mhzMap[profile][module] = 0; this->profileList->mhzMap[profile][module] = 0;
listItem->setValue(formatListFreqMHz(0)); listItem->setValue(formatListFreqMHz(0));
// Save the updated profile
Result rc = sysclkIpcSetProfiles(this->applicationId, this->profileList); Result rc = sysclkIpcSetProfiles(this->applicationId, this->profileList);
if(R_FAILED(rc)) if(R_FAILED(rc))
{ {
@@ -117,12 +142,137 @@ void AppProfileGui::addModuleListItemToggle(SysClkProfile profile, SysClkModule
this->listElement->addItem(toggle); this->listElement->addItem(toggle);
} }
void AppProfileGui::addModuleListItemValue(
SysClkProfile profile,
SysClkModule module,
const std::string& categoryName,
std::uint32_t min,
std::uint32_t max,
std::uint32_t step,
const std::string& suffix,
std::uint32_t divisor,
int decimalPlaces
)
{
tsl::elm::ListItem* listItem =
new tsl::elm::ListItem(sysclkFormatModule(module, true));
std::uint32_t storedValue = this->profileList->mhzMap[profile][module];
if (storedValue == 0) {
listItem->setValue(FREQ_DEFAULT_TEXT);
} else {
char buf[32];
if (decimalPlaces > 0) {
double displayValue = (double)storedValue / divisor;
snprintf(buf, sizeof(buf), "%.*f%s", decimalPlaces, displayValue, suffix.c_str());
} else {
snprintf(buf, sizeof(buf), "%u%s", storedValue / divisor, suffix.c_str());
}
listItem->setValue(buf);
}
listItem->setClickListener(
[this,
listItem,
profile,
module,
categoryName,
min,
max,
step,
suffix,
divisor,
decimalPlaces](u64 keys)
{
if ((keys & HidNpadButton_A) == HidNpadButton_A)
{
std::uint32_t currentValue =
this->profileList->mhzMap[profile][module] * divisor;
ValueRange range(
min,
max,
step,
suffix,
divisor,
decimalPlaces
);
this->openValueChoiceGui(
listItem,
currentValue,
range,
categoryName,
[this, listItem, profile, module, divisor, suffix, decimalPlaces](std::uint32_t value) -> bool
{
this->profileList->mhzMap[profile][module] = value / divisor;
if (value == 0) {
listItem->setValue(FREQ_DEFAULT_TEXT);
} else {
char buf[32];
if (decimalPlaces > 0) {
double displayValue = (double)value / divisor;
snprintf(buf, sizeof(buf), "%.*f%s",
decimalPlaces, displayValue, suffix.c_str());
} else {
snprintf(buf, sizeof(buf), "%u%s",
value / divisor, suffix.c_str());
}
listItem->setValue(buf);
}
Result rc =
sysclkIpcSetProfiles(this->applicationId,
this->profileList);
if (R_FAILED(rc))
{
FatalGui::openWithResultCode(
"sysclkIpcSetProfiles", rc);
return false;
}
return true;
},
ValueThresholds(),
false
);
return true;
}
else if ((keys & HidNpadButton_Y) == HidNpadButton_Y)
{
this->profileList->mhzMap[profile][module] = 0;
listItem->setValue(FREQ_DEFAULT_TEXT);
Result rc =
sysclkIpcSetProfiles(this->applicationId,
this->profileList);
if (R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetProfiles", rc);
return false;
}
return true;
}
return false;
});
this->listElement->addItem(listItem);
}
void AppProfileGui::addProfileUI(SysClkProfile profile) void AppProfileGui::addProfileUI(SysClkProfile profile)
{ {
this->listElement->addItem(new tsl::elm::CategoryHeader(sysclkFormatProfile(profile, true) + std::string(" ") + ult::DIVIDER_SYMBOL + "  Reset")); this->listElement->addItem(new tsl::elm::CategoryHeader(sysclkFormatProfile(profile, true) + std::string(" ") + ult::DIVIDER_SYMBOL + "  Reset"));
this->addModuleListItem(profile, SysClkModule_CPU); this->addModuleListItem(profile, SysClkModule_CPU);
this->addModuleListItem(profile, SysClkModule_GPU); this->addModuleListItem(profile, SysClkModule_GPU);
this->addModuleListItem(profile, SysClkModule_MEM); this->addModuleListItem(profile, SysClkModule_MEM);
this->addModuleListItemValue(profile, HorizonOCModule_Display, "Display", 40, 60, 5, " Hz", 1, 0);
this->addModuleListItemToggle(profile, HorizonOCModule_Governor); this->addModuleListItemToggle(profile, HorizonOCModule_Governor);
} }

View File

@@ -30,6 +30,7 @@
#include "../../ipc.h" #include "../../ipc.h"
#include "base_menu_gui.h" #include "base_menu_gui.h"
#include "freq_choice_gui.h" #include "freq_choice_gui.h"
#include "value_choice_gui.h"
#define SYSCLK_GLOBAL_PROFILE_TID 0xA111111111111111 #define SYSCLK_GLOBAL_PROFILE_TID 0xA111111111111111
class AppProfileGui : public BaseMenuGui class AppProfileGui : public BaseMenuGui
@@ -41,6 +42,29 @@ class AppProfileGui : public BaseMenuGui
void openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfile profile, SysClkModule module); void openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfile profile, SysClkModule module);
void addModuleListItem(SysClkProfile profile, SysClkModule module); void addModuleListItem(SysClkProfile profile, SysClkModule module);
void addModuleListItemToggle(SysClkProfile profile, SysClkModule module); void addModuleListItemToggle(SysClkProfile profile, SysClkModule module);
void openValueChoiceGui(
tsl::elm::ListItem* listItem,
std::uint32_t currentValue,
const ValueRange& range,
const std::string& categoryName,
ValueChoiceListener listener,
const ValueThresholds& thresholds = ValueThresholds(),
bool enableThresholds = false,
const std::map<std::uint32_t, std::string>& labels = {},
const std::vector<NamedValue>& namedValues = {},
bool showDefaultValue = true
);
void addModuleListItemValue(
SysClkProfile profile,
SysClkModule module,
const std::string& categoryName,
std::uint32_t min,
std::uint32_t max,
std::uint32_t step,
const std::string& suffix,
std::uint32_t divisor,
int decimalPlaces
);
void addProfileUI(SysClkProfile profile); void addProfileUI(SysClkProfile profile);
public: public:

View File

@@ -56,7 +56,7 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
// All constants pre-calculated and cached // All constants pre-calculated and cached
static constexpr const char* const labels[] = { static constexpr const char* const labels[] = {
"App ID", "Profile", "CPU", "GPU", "MEM", "SoC", "Board", "Skin", "Now", "Avg", "BAT", "PMIC", "FAN" "App ID", "Profile", "CPU", "GPU", "MEM", "SoC", "Board", "Skin", "Now", "Avg", "BAT", "PMIC", "FAN", "DISP"
}; };
static constexpr u32 dataPositions[6] = {63-3+3, 200-1, 344-1-3, 200-1, 342-1, 321-1}; static constexpr u32 dataPositions[6] = {63-3+3, 200-1, 344-1-3, 200-1, 342-1, 321-1};
@@ -163,6 +163,10 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
renderer->drawString(displayStrings[24], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // fan speed renderer->drawString(displayStrings[24], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // fan speed
renderer->drawString(labels[13], false, positions[7], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // fan label
renderer->drawString(displayStrings[25], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // fan speed
} }
// Optimized refresh - now does all the string formatting once per second // Optimized refresh - now does all the string formatting once per second
@@ -268,6 +272,8 @@ void BaseMenuGui::refresh()
sprintf(displayStrings[24], "%u%%", context->PartLoad[HocClkPartLoad_FAN]); sprintf(displayStrings[24], "%u%%", context->PartLoad[HocClkPartLoad_FAN]);
sprintf(displayStrings[25], "%u Hz", context->realFreqs[HorizonOCModule_Display]);
} }
tsl::elm::Element* BaseMenuGui::baseUI() tsl::elm::Element* BaseMenuGui::baseUI()

View File

@@ -1,39 +1,11 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#include "global_override_gui.h"
#include "fatal_gui.h"
#include "../format.h" #include "../format.h"
#include "fatal_gui.h"
#include "global_override_gui.h"
#include "value_choice_gui.h"
GlobalOverrideGui::GlobalOverrideGui() GlobalOverrideGui::GlobalOverrideGui()
{ {
for(std::uint16_t m = 0; m < SysClkModule_EnumMax; m++) for (std::uint16_t m = 0; m < SysClkModule_EnumMax; m++) {
{
this->listItems[m] = nullptr; this->listItems[m] = nullptr;
this->listHz[m] = 0; this->listHz[m] = 0;
} }
@@ -43,16 +15,17 @@ void GlobalOverrideGui::openFreqChoiceGui(SysClkModule module)
{ {
std::uint32_t hzList[SYSCLK_FREQ_LIST_MAX]; std::uint32_t hzList[SYSCLK_FREQ_LIST_MAX];
std::uint32_t hzCount; std::uint32_t hzCount;
Result rc = sysclkIpcGetFreqList(module, &hzList[0], SYSCLK_FREQ_LIST_MAX, &hzCount); Result rc =
if(R_FAILED(rc)) sysclkIpcGetFreqList(module, &hzList[0], SYSCLK_FREQ_LIST_MAX, &hzCount);
{ if (R_FAILED(rc)) {
FatalGui::openWithResultCode("sysclkIpcGetFreqList", rc); FatalGui::openWithResultCode("sysclkIpcGetFreqList", rc);
return; return;
} }
tsl::changeTo<FreqChoiceGui>(this->context->overrideFreqs[module], hzList, hzCount, module, [this, module](std::uint32_t hz) { tsl::changeTo<FreqChoiceGui>(
this->context->overrideFreqs[module], hzList, hzCount, module,
[this, module](std::uint32_t hz) {
Result rc = sysclkIpcSetOverride(module, hz); Result rc = sysclkIpcSetOverride(module, hz);
if(R_FAILED(rc)) if (R_FAILED(rc)) {
{
FatalGui::openWithResultCode("sysclkIpcSetOverride", rc); FatalGui::openWithResultCode("sysclkIpcSetOverride", rc);
return false; return false;
} }
@@ -61,78 +34,228 @@ void GlobalOverrideGui::openFreqChoiceGui(SysClkModule module)
this->context->overrideFreqs[module] = hz; this->context->overrideFreqs[module] = hz;
return true; return true;
}, true); },
true);
}
void GlobalOverrideGui::openValueChoiceGui(
tsl::elm::ListItem* listItem,
std::uint32_t currentValue,
const ValueRange& range,
const std::string& categoryName,
ValueChoiceListener listener,
const ValueThresholds& thresholds,
bool enableThresholds,
const std::map<std::uint32_t, std::string>& labels,
const std::vector<NamedValue>& namedValues,
bool showDefaultValue
)
{
tsl::changeTo<ValueChoiceGui>(
currentValue,
range,
categoryName,
listener,
thresholds,
enableThresholds,
labels,
namedValues,
showDefaultValue
);
}
void GlobalOverrideGui::addModuleListItemValue(
SysClkModule module,
const std::string& categoryName,
std::uint32_t min,
std::uint32_t max,
std::uint32_t step,
const std::string& suffix,
std::uint32_t divisor,
int decimalPlaces
)
{
this->customFormatModules[module] = std::make_tuple(suffix, divisor, decimalPlaces);
tsl::elm::ListItem* listItem =
new tsl::elm::ListItem(sysclkFormatModule(module, true));
listItem->setValue(FREQ_DEFAULT_TEXT);
listItem->setClickListener(
[this,
listItem,
module,
categoryName,
min,
max,
step,
suffix,
divisor,
decimalPlaces](u64 keys)
{
if ((keys & HidNpadButton_A) == HidNpadButton_A)
{
if (!this->context) {
return false;
}
std::uint32_t currentValue =
this->context->overrideFreqs[module] * divisor;
ValueRange range(
min,
max,
step,
suffix,
divisor,
decimalPlaces
);
this->openValueChoiceGui(
listItem,
currentValue,
range,
categoryName,
[this, listItem, module, divisor, suffix, decimalPlaces](std::uint32_t value) -> bool
{
if (!this->context) {
return false;
}
this->context->overrideFreqs[module] = value / divisor;
this->listHz[module] = value / divisor;
if (value == 0) {
listItem->setValue(FREQ_DEFAULT_TEXT);
} else {
char buf[32];
if (decimalPlaces > 0) {
double displayValue = (double)value / divisor;
snprintf(buf, sizeof(buf), "%.*f%s",
decimalPlaces, displayValue, suffix.c_str());
} else {
snprintf(buf, sizeof(buf), "%u%s",
value / divisor, suffix.c_str());
}
listItem->setValue(buf);
}
Result rc =
sysclkIpcSetOverride(module, this->context->overrideFreqs[module]);
if (R_FAILED(rc))
{
FatalGui::openWithResultCode(
"sysclkIpcSetOverride", rc);
return false;
}
this->lastContextUpdate = armGetSystemTick();
return true;
},
ValueThresholds(),
false,
std::map<std::uint32_t, std::string>(),
std::vector<NamedValue>(),
true
);
return true;
}
else if ((keys & HidNpadButton_Y) == HidNpadButton_Y)
{
if (!this->context) {
return false;
}
this->context->overrideFreqs[module] = 0;
this->listHz[module] = 0;
listItem->setValue(FREQ_DEFAULT_TEXT);
Result rc = sysclkIpcSetOverride(module, 0);
if (R_FAILED(rc))
{
FatalGui::openWithResultCode("sysclkIpcSetOverride", rc);
return false;
}
this->lastContextUpdate = armGetSystemTick();
return true;
}
return false;
});
this->listElement->addItem(listItem);
this->listItems[module] = listItem;
} }
void GlobalOverrideGui::addModuleListItem(SysClkModule module) void GlobalOverrideGui::addModuleListItem(SysClkModule module)
{ {
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(sysclkFormatModule(module, true)); tsl::elm::ListItem *listItem =
new tsl::elm::ListItem(sysclkFormatModule(module, true));
listItem->setValue(formatListFreqMHz(0)); listItem->setValue(formatListFreqMHz(0));
listItem->setClickListener([this, module](u64 keys) { listItem->setClickListener([this, module](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A) if ((keys & HidNpadButton_A) == HidNpadButton_A) {
{
this->openFreqChoiceGui(module); this->openFreqChoiceGui(module);
return true; return true;
} } else if ((keys & HidNpadButton_Y) == HidNpadButton_Y) {
else if((keys & HidNpadButton_Y) == HidNpadButton_Y)
{
// Reset override to "Default" (0 Hz)
Result rc = sysclkIpcSetOverride(module, 0); Result rc = sysclkIpcSetOverride(module, 0);
if(R_FAILED(rc)) if (R_FAILED(rc)) {
{
FatalGui::openWithResultCode("sysclkIpcSetOverride", rc); FatalGui::openWithResultCode("sysclkIpcSetOverride", rc);
return false; return false;
} }
// Update context and tracking variables
this->lastContextUpdate = armGetSystemTick(); this->lastContextUpdate = armGetSystemTick();
this->context->overrideFreqs[module] = 0; this->context->overrideFreqs[module] = 0;
this->listHz[module] = 0; this->listHz[module] = 0;
// Update display
this->listItems[module]->setValue(formatListFreqHz(0)); this->listItems[module]->setValue(formatListFreqHz(0));
return true; return true;
} }
return false; return false;
}); });
this->listElement->addItem(listItem); this->listElement->addItem(listItem);
this->listItems[module] = listItem; this->listItems[module] = listItem;
} }
void GlobalOverrideGui::addModuleToggleItem(SysClkModule module) void GlobalOverrideGui::addModuleToggleItem(SysClkModule module)
{ {
const char* moduleName = sysclkFormatModule(module, true); const char *moduleName = sysclkFormatModule(module, true);
bool isOn = this->listHz[module]; bool isOn = this->listHz[module];
// Create a ToggleListItem
tsl::elm::ToggleListItem* toggle = new tsl::elm::ToggleListItem(moduleName, isOn);
toggle->setStateChangedListener([this, module, toggle](bool state) {
tsl::elm::ToggleListItem *toggle =
new tsl::elm::ToggleListItem(moduleName, isOn);
toggle->setStateChangedListener([this, module, toggle](bool state) {
Result rc = sysclkIpcSetOverride(module, state ? 1 : 0); Result rc = sysclkIpcSetOverride(module, state ? 1 : 0);
if(R_FAILED(rc)) if (R_FAILED(rc)) {
{
FatalGui::openWithResultCode("sysclkIpcSetProfiles", rc); FatalGui::openWithResultCode("sysclkIpcSetProfiles", rc);
} }
this->lastContextUpdate = armGetSystemTick(); this->lastContextUpdate = armGetSystemTick();
this->context->overrideFreqs[module] = 0; this->context->overrideFreqs[module] = 0;
this->listHz[module] = 0; this->listHz[module] = 0;
}); });
// Add to list and track
this->listElement->addItem(toggle); this->listElement->addItem(toggle);
this->listItems[module] = toggle; this->listItems[module] = toggle;
} }
void GlobalOverrideGui::listUI() void GlobalOverrideGui::listUI()
{ {
this->listElement->addItem(new tsl::elm::CategoryHeader("Temporary Overrides " + ult::DIVIDER_SYMBOL + "  Reset")); this->listElement->addItem(new tsl::elm::CategoryHeader(
"Temporary Overrides " + ult::DIVIDER_SYMBOL + " Reset"));
this->addModuleListItem(SysClkModule_CPU); this->addModuleListItem(SysClkModule_CPU);
this->addModuleListItem(SysClkModule_GPU); this->addModuleListItem(SysClkModule_GPU);
this->addModuleListItem(SysClkModule_MEM); this->addModuleListItem(SysClkModule_MEM);
this->addModuleListItemValue(HorizonOCModule_Display, "Display", 40, 60, 5, " Hz", 1, 0);
this->addModuleToggleItem(HorizonOCModule_Governor); this->addModuleToggleItem(HorizonOCModule_Governor);
} }
@@ -143,18 +266,16 @@ void GlobalOverrideGui::refresh()
if (!this->context) if (!this->context)
return; return;
for (std::uint16_t m = 0; m < SysClkModule_EnumMax; m++) for (std::uint16_t m = 0; m < SysClkModule_EnumMax; m++) {
{ if (m == HorizonOCModule_Governor) {
if (m == HorizonOCModule_Governor) auto *toggle =
{ static_cast<tsl::elm::ToggleListItem *>(this->listItems[m]);
auto* toggle = static_cast<tsl::elm::ToggleListItem*>(this->listItems[m]);
if (!toggle) if (!toggle)
continue; continue;
bool newState = this->context->overrideFreqs[m] != 0; bool newState = this->context->overrideFreqs[m] != 0;
if (toggle->getState() != newState) if (toggle->getState() != newState) {
{
toggle->setState(newState); toggle->setState(newState);
} }
@@ -162,11 +283,33 @@ void GlobalOverrideGui::refresh()
} }
if (this->listItems[m] != nullptr && if (this->listItems[m] != nullptr &&
this->listHz[m] != this->context->overrideFreqs[m]) this->listHz[m] != this->context->overrideFreqs[m]) {
{
this->listItems[m]->setValue( auto it = this->customFormatModules.find((SysClkModule)m);
formatListFreqHz(this->context->overrideFreqs[m]) if (it != this->customFormatModules.end()) {
); std::string suffix = std::get<0>(it->second);
std::uint32_t divisor = std::get<1>(it->second);
int decimalPlaces = std::get<2>(it->second);
if (this->context->overrideFreqs[m] == 0) {
this->listItems[m]->setValue(FREQ_DEFAULT_TEXT);
} else {
char buf[32];
if (decimalPlaces > 0) {
double displayValue = (double)this->context->overrideFreqs[m] / divisor;
snprintf(buf, sizeof(buf), "%.*f%s",
decimalPlaces, displayValue, suffix.c_str());
} else {
snprintf(buf, sizeof(buf), "%u%s",
this->context->overrideFreqs[m] / divisor, suffix.c_str());
}
this->listItems[m]->setValue(buf);
}
} else {
this->listItems[m]->setValue(
formatListFreqHz(this->context->overrideFreqs[m]));
}
this->listHz[m] = this->context->overrideFreqs[m]; this->listHz[m] = this->context->overrideFreqs[m];
} }
} }

View File

@@ -30,19 +30,44 @@
#include "../../ipc.h" #include "../../ipc.h"
#include "base_menu_gui.h" #include "base_menu_gui.h"
#include "freq_choice_gui.h" #include "freq_choice_gui.h"
#include <string>
#include "value_choice_gui.h"
class GlobalOverrideGui : public BaseMenuGui class GlobalOverrideGui : public BaseMenuGui
{ {
protected: protected:
std::map<SysClkModule, std::tuple<std::string, std::uint32_t, int>> customFormatModules;
tsl::elm::ListItem* listItems[SysClkModule_EnumMax]; tsl::elm::ListItem* listItems[SysClkModule_EnumMax];
std::uint32_t listHz[SysClkModule_EnumMax]; std::uint32_t listHz[SysClkModule_EnumMax];
bool isGovernorEnabled; bool isGovernorEnabled;
void openFreqChoiceGui(SysClkModule module); void openFreqChoiceGui(SysClkModule module);
void addModuleListItem(SysClkModule module); void addModuleListItem(SysClkModule module);
void addModuleToggleItem(SysClkModule module); void addModuleToggleItem(SysClkModule module);
void openValueChoiceGui(
tsl::elm::ListItem* listItem,
std::uint32_t currentValue,
const ValueRange& range,
const std::string& categoryName,
ValueChoiceListener listener,
const ValueThresholds& thresholds,
bool enableThresholds,
const std::map<std::uint32_t, std::string>& labels,
const std::vector<NamedValue>& namedValues,
bool showDefaultValue
);
void addModuleListItemValue(
SysClkModule module,
const std::string& categoryName,
std::uint32_t min,
std::uint32_t max,
std::uint32_t step,
const std::string& suffix,
std::uint32_t divisor,
int decimalPlaces
);
public: public:
GlobalOverrideGui(); GlobalOverrideGui();
~GlobalOverrideGui() {} ~GlobalOverrideGui() {}
void listUI() override; void listUI() override;
void refresh() override; void refresh() override;
void setModuleCustomFormat(SysClkModule module, const std::string& suffix, std::uint32_t divisor, int decimalPlaces);
}; };

View File

@@ -229,15 +229,14 @@ void Board::Initialize()
} }
u64 clkVirtAddr, dsiVirtAddr, outsize; u64 clkVirtAddr, dsiVirtAddr, outsize;
Result rc = svcQueryMemoryMapping(&clkVirtAddr, &outsize, 0x60006000, 0x1000); rc = svcQueryMemoryMapping(&clkVirtAddr, &outsize, 0x60006000, 0x1000);
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (clk)"); ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (clk)");
Result rc = svcQueryMemoryMapping(&dsiVirtAddr, &outsize, 0x54300000, 0x40000); rc = svcQueryMemoryMapping(&dsiVirtAddr, &outsize, 0x54300000, 0x40000);
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (dsi)"); ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (dsi)");
DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr}; DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr};
DisplayRefresh_Initialize(&cfg); DisplayRefresh_Initialize(&cfg);
FetchHardwareInfos(); FetchHardwareInfos();
} }
@@ -308,6 +307,11 @@ void Board::SetHz(SysClkModule module, std::uint32_t hz)
{ {
Result rc = 0; Result rc = 0;
if(module == HorizonOCModule_Display) {
DisplayRefresh_SetRate(hz);
return;
}
if(HOSSVC_HAS_CLKRST) if(HOSSVC_HAS_CLKRST)
{ {
ClkrstSession session = {0}; ClkrstSession session = {0};
@@ -332,6 +336,11 @@ std::uint32_t Board::GetHz(SysClkModule module)
Result rc = 0; Result rc = 0;
std::uint32_t hz = 0; std::uint32_t hz = 0;
if(module == HorizonOCModule_Display) {
DisplayRefresh_GetRate(&hz, false);
return hz;
}
if(HOSSVC_HAS_CLKRST) if(HOSSVC_HAS_CLKRST)
{ {
ClkrstSession session = {0}; ClkrstSession session = {0};
@@ -355,6 +364,7 @@ std::uint32_t Board::GetHz(SysClkModule module)
std::uint32_t Board::GetRealHz(SysClkModule module) std::uint32_t Board::GetRealHz(SysClkModule module)
{ {
u32 hz = 0;
switch(module) switch(module)
{ {
case SysClkModule_CPU: case SysClkModule_CPU:
@@ -363,6 +373,9 @@ std::uint32_t Board::GetRealHz(SysClkModule module)
return t210ClkGpuFreq(); return t210ClkGpuFreq();
case SysClkModule_MEM: case SysClkModule_MEM:
return t210ClkMemFreq(); return t210ClkMemFreq();
case HorizonOCModule_Display:
DisplayRefresh_GetRate(&hz, false);
return hz;
default: default:
ASSERT_ENUM_VALID(SysClkModule, module); ASSERT_ENUM_VALID(SysClkModule, module);
} }
@@ -377,6 +390,8 @@ void Board::GetFreqList(SysClkModule module, std::uint32_t* outList, std::uint32
s32 tmpInMaxCount = maxCount; s32 tmpInMaxCount = maxCount;
s32 tmpOutCount = 0; s32 tmpOutCount = 0;
if(HOSSVC_HAS_CLKRST) if(HOSSVC_HAS_CLKRST)
{ {
ClkrstSession session = {0}; ClkrstSession session = {0};
@@ -552,6 +567,11 @@ void Board::ResetToStockGpu()
ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode");
} }
} }
void Board::ResetToStockDisplay() {
DisplayRefresh_SetRate(60);
}
std::uint32_t Board::GetTemperatureMilli(SysClkThermalSensor sensor) std::uint32_t Board::GetTemperatureMilli(SysClkThermalSensor sensor)
{ {
std::int32_t millis = 0; std::int32_t millis = 0;

View File

@@ -43,6 +43,7 @@ class Board
static void ResetToStockCpu(); static void ResetToStockCpu();
static void ResetToStockMem(); static void ResetToStockMem();
static void ResetToStockGpu(); static void ResetToStockGpu();
static void ResetToStockDisplay();
static SysClkProfile GetProfile(); static SysClkProfile GetProfile();
static void SetHz(SysClkModule module, std::uint32_t hz); static void SetHz(SysClkModule module, std::uint32_t hz);

View File

@@ -312,7 +312,8 @@ void ClockManager::GovernorThread(void* arg)
targetHz = mgr->config->GetAutoClockHz( targetHz = mgr->config->GetAutoClockHz(
mgr->context->applicationId, mgr->context->applicationId,
SysClkModule_GPU, SysClkModule_GPU,
mgr->context->profile mgr->context->profile,
false
); );
if (!targetHz) if (!targetHz)
@@ -320,7 +321,8 @@ void ClockManager::GovernorThread(void* arg)
targetHz = mgr->config->GetAutoClockHz( targetHz = mgr->config->GetAutoClockHz(
GLOBAL_PROFILE_ID, GLOBAL_PROFILE_ID,
SysClkModule_GPU, SysClkModule_GPU,
mgr->context->profile mgr->context->profile,
false
); );
} }
} }
@@ -410,15 +412,19 @@ void ClockManager::Tick()
ResetToStockClocks(); ResetToStockClocks();
return; return;
} }
bool returnRaw = false;
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
{ {
if(module > SysClkModule_MEM)
returnRaw = true;
else
returnRaw = false;
targetHz = this->context->overrideFreqs[module]; targetHz = this->context->overrideFreqs[module];
if (!targetHz) if (!targetHz)
{ {
targetHz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile); targetHz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile, returnRaw);
if(!targetHz) if(!targetHz)
targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, (SysClkModule)module, this->context->profile); targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, (SysClkModule)module, this->context->profile, returnRaw);
} }
if(module == HorizonOCModule_Governor) { if(module == HorizonOCModule_Governor) {
@@ -427,14 +433,20 @@ void ClockManager::Tick()
FileUtils::LogLine("[mgr] Governor state changed: %s", newGovernorState ? "enabled" : "disabled"); FileUtils::LogLine("[mgr] Governor state changed: %s", newGovernorState ? "enabled" : "disabled");
lastGovernorState = newGovernorState; lastGovernorState = newGovernorState;
// Force a "context refresh" like on app/profile change
hasChanged = true; hasChanged = true;
this->context->enabled = this->GetConfig()->Enabled(); this->context->enabled = this->GetConfig()->Enabled();
Board::ResetToStock(); // optional: reset clocks before re-applying Board::ResetToStock();
} }
isGovernorEnabled = newGovernorState; isGovernorEnabled = newGovernorState;
} }
if(module == HorizonOCModule_Display) {
if(targetHz)
Board::SetHz(HorizonOCModule_Display, targetHz);
else
Board::ResetToStockDisplay();
}
// Skip GPU if governor handles it // Skip GPU if governor handles it
if(module > SysClkModule_MEM) { if(module > SysClkModule_MEM) {
continue; continue;

View File

@@ -138,7 +138,7 @@ std::uint32_t Config::FindClockMHz(std::uint64_t tid, SysClkModule module, SysCl
return 0; return 0;
} }
std::uint32_t Config::FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles) std::uint32_t Config::FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles, u32 mhzMultiplier)
{ {
std::uint32_t mhz = 0; std::uint32_t mhz = 0;
@@ -155,23 +155,23 @@ std::uint32_t Config::FindClockHzFromProfiles(std::uint64_t tid, SysClkModule mo
} }
} }
return std::max((std::uint32_t)0, mhz * 1000000); return std::max((std::uint32_t)0, mhz * mhzMultiplier);
} }
std::uint32_t Config::GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile) std::uint32_t Config::GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile, bool returnRaw)
{ {
std::scoped_lock lock{this->configMutex}; std::scoped_lock lock{this->configMutex};
switch(profile) switch(profile)
{ {
case SysClkProfile_Handheld: case SysClkProfile_Handheld:
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Handheld}); return FindClockHzFromProfiles(tid, module, {SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
case SysClkProfile_HandheldCharging: case SysClkProfile_HandheldCharging:
case SysClkProfile_HandheldChargingUSB: case SysClkProfile_HandheldChargingUSB:
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingUSB, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}); return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingUSB, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
case SysClkProfile_HandheldChargingOfficial: case SysClkProfile_HandheldChargingOfficial:
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingOfficial, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}); return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingOfficial, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
case SysClkProfile_Docked: case SysClkProfile_Docked:
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Docked}); return FindClockHzFromProfiles(tid, module, {SysClkProfile_Docked}, returnRaw ? 1 : 1000000);
default: default:
ERROR_THROW("Unhandled SysClkProfile: %u", profile); ERROR_THROW("Unhandled SysClkProfile: %u", profile);
} }

View File

@@ -54,7 +54,7 @@ class Config
std::uint8_t GetProfileCount(std::uint64_t tid); std::uint8_t GetProfileCount(std::uint64_t tid);
void GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles); void GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles);
bool SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate); bool SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate);
std::uint32_t GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile); std::uint32_t GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile, bool returnRaw);
void SetEnabled(bool enabled); void SetEnabled(bool enabled);
bool Enabled(); bool Enabled();
@@ -76,7 +76,7 @@ class Config
bool kipOverride[SysClkConfigValue_EnumMax]; bool kipOverride[SysClkConfigValue_EnumMax];
time_t CheckModificationTime(); time_t CheckModificationTime();
std::uint32_t FindClockMHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile); std::uint32_t FindClockMHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile);
std::uint32_t FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles); std::uint32_t FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles, u32 mhzMultiplier = 1000000);
static int BrowseIniFunc(const char* section, const char* key, const char* value, void* userdata); static int BrowseIniFunc(const char* section, const char* key, const char* value, void* userdata);
std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t> profileMHzMap; std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t> profileMHzMap;