Compare commits
63 Commits
hocclk-on-
...
1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9558fd67bb | ||
|
|
59b62a835b | ||
|
|
ad6847ec59 | ||
|
|
061c255978 | ||
|
|
bb44d0907d | ||
|
|
2215b17a54 | ||
|
|
cc52eab3f6 | ||
|
|
155a4ce50a | ||
|
|
17739377bd | ||
|
|
782fae1624 | ||
|
|
59ca1f18d6 | ||
|
|
c6cda6eb30 | ||
|
|
9f3c5d8de6 | ||
|
|
e4c9ac6ee3 | ||
|
|
5d6500523b | ||
|
|
b111ccabb5 | ||
|
|
b90a67aa43 | ||
|
|
9ed72839f4 | ||
|
|
b8f50da45f | ||
|
|
1e4dada672 | ||
|
|
a943d14807 | ||
|
|
2cf437ff34 | ||
|
|
7cbccbbb5b | ||
|
|
f79af5b6f7 | ||
|
|
05af1d04ff | ||
|
|
2753646f06 | ||
|
|
2b3889c897 | ||
|
|
e01e346dea | ||
|
|
7735037ad9 | ||
|
|
b2bcd5fc3a | ||
|
|
a40ea357db | ||
|
|
7434c22772 | ||
|
|
f0eb25b88c | ||
|
|
7ec9827db5 | ||
|
|
eeca31c92e | ||
|
|
2cd736035c | ||
|
|
bc99616e43 | ||
|
|
732f27fcf6 | ||
|
|
54e8465e47 | ||
|
|
7ceb02c001 | ||
|
|
2450a348f1 | ||
|
|
7bd469939e | ||
|
|
89180359c0 | ||
|
|
4259ace5c4 | ||
|
|
d7e5c38a62 | ||
|
|
b601105998 | ||
|
|
ca07d0716f | ||
|
|
eb16df3450 | ||
|
|
b44684760d | ||
|
|
be3fc1bb84 | ||
|
|
e781e67b43 | ||
|
|
40800ffe4b | ||
|
|
4e704e59e8 | ||
|
|
06e5d5e3d1 | ||
|
|
06010d7cd6 | ||
|
|
2549cd9a71 | ||
|
|
e89e35436e | ||
|
|
f5029ee3e9 | ||
|
|
08a84e0a8b | ||
|
|
36c819de04 | ||
|
|
f62987af4b | ||
|
|
8d9b44d6ec | ||
|
|
a5babb722d |
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
<img src="assets/logo.png" alt="logo" width="350"/>
|
<img src="assets/logo.png" alt="logo" width="768"/>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
|
|||||||
* Built-in configurator
|
* Built-in configurator
|
||||||
* Compatible with most homebrew
|
* Compatible with most homebrew
|
||||||
|
|
||||||
> It is reccomended to read the [guide](https://rentry.co/howtoget60fps) before proceeding, as this can help you get a *significant* performance boost over the default settings, often times with less power draw and heat output
|
> It is recommended to read the [guide](https://rentry.co/howtoget60fps) before proceeding, as this can help you get a *significant* performance boost over the default settings, often times with less power draw and heat output
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Atmosphère-NX
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define __ACCESS_TABLE_NAME__ SocthermAccessTable
|
|
||||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceSoctherm.GetAddress()
|
|
||||||
#define __ACCESS_TABLE_INC__ "secmon_soctherm_access_table_data.inc"
|
|
||||||
|
|
||||||
#include "secmon_define_access_table.inc"
|
|
||||||
|
|
||||||
#undef __ACCESS_TABLE_INC__
|
|
||||||
#undef __ACCESS_TABLE_ADDRESS__
|
|
||||||
#undef __ACCESS_TABLE_NAME__
|
|
||||||
@@ -123,7 +123,7 @@ namespace ams::secmon {
|
|||||||
constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCode = MemoryRegion(UINT64_C(0x40020000), 0x20000);
|
constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCode = MemoryRegion(UINT64_C(0x40020000), 0x20000);
|
||||||
static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramBootCode));
|
static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramBootCode));
|
||||||
|
|
||||||
constexpr inline const MemoryRegion MemoryRegionVirtualDevice = MemoryRegion(UINT64_C(0x1F0040000), UINT64_C(0x40000 + 0x2000));
|
constexpr inline const MemoryRegion MemoryRegionVirtualDevice = MemoryRegion(UINT64_C(0x1F0040000), UINT64_C(0x40000));
|
||||||
static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDevice));
|
static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDevice));
|
||||||
|
|
||||||
constexpr inline const MemoryRegion MemoryRegionVirtualDeviceEmpty = MemoryRegion(MemoryRegionVirtualDevice.GetStartAddress(), 0);
|
constexpr inline const MemoryRegion MemoryRegionVirtualDeviceEmpty = MemoryRegion(MemoryRegionVirtualDevice.GetStartAddress(), 0);
|
||||||
@@ -155,9 +155,6 @@ namespace ams::secmon {
|
|||||||
HANDLER(Disp1, Sdmmc, UINT64_C(0x54200000), UINT64_C(0x3000), true, ## __VA_ARGS__) \
|
HANDLER(Disp1, Sdmmc, UINT64_C(0x54200000), UINT64_C(0x3000), true, ## __VA_ARGS__) \
|
||||||
HANDLER(Dsi, Disp1, UINT64_C(0x54300000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
HANDLER(Dsi, Disp1, UINT64_C(0x54300000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||||
HANDLER(MipiCal, Dsi, UINT64_C(0x700E3000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
HANDLER(MipiCal, Dsi, UINT64_C(0x700E3000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||||
HANDLER(Soctherm, MipiCal, UINT64_C(0x700E2000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
|
||||||
HANDLER(ExternalMemoryController1, Soctherm, UINT64_C(0x7001e000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
|
||||||
HANDLER(ExternalMemoryController2, ExternalMemoryController1, UINT64_C(0x7001f000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
|
||||||
|
|
||||||
#define DEFINE_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \
|
#define DEFINE_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \
|
||||||
constexpr inline const MemoryRegion MemoryRegionVirtualDevice##_NAME_ = MemoryRegion(MemoryRegionVirtualDevice##_PREV_.GetEndAddress() + 0x1000, _SIZE_); \
|
constexpr inline const MemoryRegion MemoryRegionVirtualDevice##_NAME_ = MemoryRegion(MemoryRegionVirtualDevice##_PREV_.GetEndAddress() + 0x1000, _SIZE_); \
|
||||||
|
|||||||
268
Source/Atmosphere-Patches/secmon_smc_handler.cpp
Normal file
268
Source/Atmosphere-Patches/secmon_smc_handler.cpp
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#include "../secmon_error.hpp"
|
||||||
|
#include "../secmon_misc.hpp"
|
||||||
|
#include "secmon_smc_common.hpp"
|
||||||
|
#include "secmon_smc_handler.hpp"
|
||||||
|
#include "secmon_smc_aes.hpp"
|
||||||
|
#include "secmon_smc_carveout.hpp"
|
||||||
|
#include "secmon_smc_device_unique_data.hpp"
|
||||||
|
#include "secmon_smc_error.hpp"
|
||||||
|
#include "secmon_smc_info.hpp"
|
||||||
|
#include "secmon_smc_memory_access.hpp"
|
||||||
|
#include "secmon_smc_power_management.hpp"
|
||||||
|
#include "secmon_smc_random.hpp"
|
||||||
|
#include "secmon_smc_register_access.hpp"
|
||||||
|
#include "secmon_smc_result.hpp"
|
||||||
|
#include "secmon_smc_rsa.hpp"
|
||||||
|
|
||||||
|
namespace ams::secmon::smc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct HandlerInfo {
|
||||||
|
u32 function_id;
|
||||||
|
u32 restriction_mask;
|
||||||
|
SmcHandler handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HandlerTable {
|
||||||
|
const HandlerInfo *entries;
|
||||||
|
size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HandlerType : int {
|
||||||
|
HandlerType_User = 0,
|
||||||
|
HandlerType_Kern = 1,
|
||||||
|
HandlerType_Count = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Restriction {
|
||||||
|
Restriction_None = (0 << 0),
|
||||||
|
Restriction_Normal = (1 << 0),
|
||||||
|
Restriction_DeviceUniqueDataNotAllowed = (1 << 1),
|
||||||
|
Restriction_SafeModeNotAllowed = (1 << 2),
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SmcCallRange {
|
||||||
|
SmcCallRange_ArmArch = 0,
|
||||||
|
SmcCallRange_Cpu = 1,
|
||||||
|
SmcCallRange_Sip = 2,
|
||||||
|
SmcCallRange_Oem = 3,
|
||||||
|
SmcCallRange_Standard = 4,
|
||||||
|
|
||||||
|
SmcCallRange_TrustedApp = 0x30,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SmcArgumentType {
|
||||||
|
ArgumentType_Integer = 0,
|
||||||
|
ArgumentType_Pointer = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SmcConvention {
|
||||||
|
Convention_Smc32 = 0,
|
||||||
|
Convention_Smc64 = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SmcCallType {
|
||||||
|
SmcCallType_YieldingCall = 0,
|
||||||
|
SmcCallType_FastCall = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SmcFunctionId {
|
||||||
|
using FunctionId = util::BitPack64::Field< 0, 8, u32>;
|
||||||
|
using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>;
|
||||||
|
using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>;
|
||||||
|
using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>;
|
||||||
|
using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>;
|
||||||
|
using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>;
|
||||||
|
using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>;
|
||||||
|
using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>;
|
||||||
|
using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>;
|
||||||
|
using Reserved = util::BitPack64::Field<16, 8, u32>;
|
||||||
|
using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>;
|
||||||
|
using Convention = util::BitPack64::Field<30, 1, SmcConvention>;
|
||||||
|
using CallType = util::BitPack64::Field<31, 1, SmcCallType>;
|
||||||
|
using Reserved2 = util::BitPack64::Field<32, 32, u32>;
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit HandlerInfo g_user_handlers[] = {
|
||||||
|
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||||
|
{ 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig },
|
||||||
|
{ 0xC3000002, Restriction_Normal, SmcGetConfigUser },
|
||||||
|
{ 0xC3000003, Restriction_Normal, SmcGetResult },
|
||||||
|
{ 0xC3000404, Restriction_Normal, SmcGetResultData },
|
||||||
|
{ 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate },
|
||||||
|
{ 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes },
|
||||||
|
{ 0xC3000007, Restriction_Normal, SmcGenerateAesKek },
|
||||||
|
{ 0xC3000008, Restriction_Normal, SmcLoadAesKey },
|
||||||
|
{ 0xC3000009, Restriction_Normal, SmcComputeAes },
|
||||||
|
{ 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey },
|
||||||
|
{ 0xC300040B, Restriction_Normal, SmcComputeCmac },
|
||||||
|
{ 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData },
|
||||||
|
{ 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData },
|
||||||
|
{ 0xC300000E, Restriction_SafeModeNotAllowed, nullptr },
|
||||||
|
{ 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
|
||||||
|
{ 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
|
||||||
|
{ 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
|
||||||
|
{ 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Deprecated handlerss. */
|
||||||
|
constexpr inline const HandlerInfo DecryptAndImportEsDeviceKeyHandlerInfo = {
|
||||||
|
0xC300100C, Restriction_Normal, SmcDecryptAndImportEsDeviceKey
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr inline const HandlerInfo DecryptAndImportLotusKeyHandlerInfo = {
|
||||||
|
0xC300100E, Restriction_SafeModeNotAllowed, SmcDecryptAndImportLotusKey
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit HandlerInfo g_kern_handlers[] = {
|
||||||
|
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||||
|
{ 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu },
|
||||||
|
{ 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu },
|
||||||
|
{ 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu },
|
||||||
|
{ 0xC3000004, Restriction_Normal, SmcGetConfigKern },
|
||||||
|
{ 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking },
|
||||||
|
{ 0xC3000006, Restriction_Normal, SmcShowError },
|
||||||
|
{ 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion },
|
||||||
|
{ 0xC3000008, Restriction_Normal, SmcReadWriteRegister },
|
||||||
|
|
||||||
|
/* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */
|
||||||
|
{ 0xC3000409, Restriction_Normal, SmcSetConfig },
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit HandlerInfo g_ams_handlers[] = {
|
||||||
|
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||||
|
{ 0xF0000201, Restriction_None, SmcIramCopy },
|
||||||
|
{ 0xF0000002, Restriction_None, SmcReadWriteRegister },
|
||||||
|
{ 0xF0000003, Restriction_None, SmcWriteAddress },
|
||||||
|
{ 0xF0000404, Restriction_None, SmcGetEmummcConfig },
|
||||||
|
{ 0xF0000005, Restriction_None, SmcShowError },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const HandlerInfo GetSecureDataHandlerInfo = {
|
||||||
|
0x67891234, Restriction_None, SmcGetSecureData
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit HandlerTable g_handler_tables[] = {
|
||||||
|
{ g_user_handlers, util::size(g_user_handlers) },
|
||||||
|
{ g_kern_handlers, util::size(g_kern_handlers) },
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit HandlerTable g_ams_handler_table = {
|
||||||
|
g_ams_handlers, util::size(g_ams_handlers)
|
||||||
|
};
|
||||||
|
|
||||||
|
NORETURN void InvalidSmcError(u64 id) {
|
||||||
|
SetError(pkg1::ErrorInfo_UnknownSmc);
|
||||||
|
AMS_ABORT("Invalid SMC: %lx", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const HandlerTable &GetHandlerTable(HandlerType type, u64 id) {
|
||||||
|
/* Ensure we have a valid handler type. */
|
||||||
|
if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) {
|
||||||
|
InvalidSmcError(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Provide support for legacy SmcGetSecureData. */
|
||||||
|
if (id == GetSecureDataHandlerInfo.function_id) {
|
||||||
|
return g_handler_tables[HandlerType_User];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're a user SMC. */
|
||||||
|
if (type == HandlerType_User) {
|
||||||
|
/* Nintendo uses OEM SMCs. */
|
||||||
|
/* We will assign Atmosphere extension SMCs the TrustedApplication range. */
|
||||||
|
if (util::BitPack64{id}.Get<SmcFunctionId::CallRange>() == SmcCallRange_TrustedApp) {
|
||||||
|
return g_ams_handler_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */
|
||||||
|
if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) {
|
||||||
|
InvalidSmcError(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_handler_tables[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) {
|
||||||
|
/* Provide support for legacy SmcGetSecureData. */
|
||||||
|
if (id == GetSecureDataHandlerInfo.function_id) {
|
||||||
|
return GetSecureDataHandlerInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get and check the index. */
|
||||||
|
const auto index = util::BitPack64{id}.Get<SmcFunctionId::FunctionId>();
|
||||||
|
if (AMS_UNLIKELY(index >= table.count)) {
|
||||||
|
InvalidSmcError(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get and check the handler info. */
|
||||||
|
const auto &handler_info = table.entries[index];
|
||||||
|
|
||||||
|
/* Check that the handler isn't null. */
|
||||||
|
if (AMS_UNLIKELY(handler_info.handler == nullptr)) {
|
||||||
|
InvalidSmcError(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the handler's id matches. */
|
||||||
|
if (AMS_UNLIKELY(handler_info.function_id != id)) {
|
||||||
|
InvalidSmcError(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsHandlerRestricted(const HandlerInfo &info) {
|
||||||
|
return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) {
|
||||||
|
/* Check if the smc is restricted. */
|
||||||
|
if (GetTargetFirmware() >= TargetFirmware_8_0_0 && AMS_UNLIKELY(IsHandlerRestricted(info))) {
|
||||||
|
return SmcResult::NotPermitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invoke the smc. */
|
||||||
|
return info.handler(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureSmcHandlersForTargetFirmware() {
|
||||||
|
const auto target_fw = GetTargetFirmware();
|
||||||
|
|
||||||
|
if (target_fw < TargetFirmware_5_0_0) {
|
||||||
|
g_user_handlers[DecryptAndImportEsDeviceKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportEsDeviceKeyHandlerInfo;
|
||||||
|
g_user_handlers[DecryptAndImportLotusKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportLotusKeyHandlerInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleSmc(int type, SmcArguments &args) {
|
||||||
|
/* Get the table. */
|
||||||
|
const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]);
|
||||||
|
|
||||||
|
/* Get the handler info. */
|
||||||
|
const auto &info = GetHandlerInfo(table, args.r[0]);
|
||||||
|
|
||||||
|
/* Set the invocation result. */
|
||||||
|
args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -101,7 +101,6 @@ namespace ams::secmon::smc {
|
|||||||
#include "secmon_define_emc_access_table.inc"
|
#include "secmon_define_emc_access_table.inc"
|
||||||
#include "secmon_define_emc1_access_table.inc"
|
#include "secmon_define_emc1_access_table.inc"
|
||||||
#include "secmon_define_emc2_access_table.inc"
|
#include "secmon_define_emc2_access_table.inc"
|
||||||
#include "secmon_define_soctherm_access_table.inc"
|
|
||||||
#include "secmon_define_mc01_access_table.inc"
|
#include "secmon_define_mc01_access_table.inc"
|
||||||
|
|
||||||
constexpr const AccessTableEntry AccessTables[] = {
|
constexpr const AccessTableEntry AccessTables[] = {
|
||||||
@@ -110,7 +109,6 @@ namespace ams::secmon::smc {
|
|||||||
{ EmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController.GetAddress(), EmcAccessTable::Address, EmcAccessTable::Size, },
|
{ EmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController.GetAddress(), EmcAccessTable::Address, EmcAccessTable::Size, },
|
||||||
{ EmcAccessTable1::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController1.GetAddress(), EmcAccessTable1::Address, EmcAccessTable1::Size, },
|
{ EmcAccessTable1::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController1.GetAddress(), EmcAccessTable1::Address, EmcAccessTable1::Size, },
|
||||||
{ EmcAccessTable2::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController2.GetAddress(), EmcAccessTable2::Address, EmcAccessTable2::Size, },
|
{ EmcAccessTable2::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController2.GetAddress(), EmcAccessTable2::Address, EmcAccessTable2::Size, },
|
||||||
{ SocthermAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceSoctherm.GetAddress(), SocthermAccessTable::Address, SocthermAccessTable::Size, },
|
|
||||||
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, },
|
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, },
|
||||||
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, },
|
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, },
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -98,7 +98,7 @@ volatile CustomizeTable C = {
|
|||||||
.eristaCpuBoostClock = 1785000, // Default boost clock
|
.eristaCpuBoostClock = 1785000, // Default boost clock
|
||||||
.marikoCpuBoostClock = 1963000, // Default boost clock
|
.marikoCpuBoostClock = 1963000, // Default boost clock
|
||||||
|
|
||||||
.eristaGpuUV = 2,
|
.eristaGpuUV = 0,
|
||||||
.eristaGpuVmin = 810,
|
.eristaGpuVmin = 810,
|
||||||
|
|
||||||
.marikoGpuUV = 0,
|
.marikoGpuUV = 0,
|
||||||
@@ -199,9 +199,9 @@ volatile CustomizeTable C = {
|
|||||||
{ 1581000, { 1130000, }, { 2889664, -122173, 1834, } },
|
{ 1581000, { 1130000, }, { 2889664, -122173, 1834, } },
|
||||||
{ 1683000, { 1168000, }, { 5100873, -279186, 4747, } },
|
{ 1683000, { 1168000, }, { 5100873, -279186, 4747, } },
|
||||||
{ 1785000, { 1225000, }, { 5100873, -279186, 4747, } },
|
{ 1785000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||||
// { 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
|
{ 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||||
// { 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
|
{ 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||||
// { 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
|
{ 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
|
||||||
},
|
},
|
||||||
|
|
||||||
.eristaCpuDvfsTableSLT = {
|
.eristaCpuDvfsTableSLT = {
|
||||||
|
|||||||
@@ -99,4 +99,25 @@ namespace ams::ldr::hoc {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace panic {
|
||||||
|
/* Requires modifying g_ams_handlers in secmon_smc_handler.cpp */
|
||||||
|
constexpr inline void SmcError(u32 rgb) {
|
||||||
|
SecmonArgs args = {};
|
||||||
|
constexpr u32 SmcShowErrorID = 0xF0000005;
|
||||||
|
args.X[0] = SmcShowErrorID;
|
||||||
|
args.X[1] = rgb;
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline u32 PackCode(u32 r, u32 g, u32 b) {
|
||||||
|
return ((r & 0xF) << 8) | ((g & 0xF) << 4) | ((b & 0xF) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 Gpu = PackCode(0xF, 0x7, 0x0);
|
||||||
|
constexpr u32 Cpu = PackCode(0xF, 0x0, 0x0);
|
||||||
|
constexpr u32 Emc = PackCode(0x0, 0xF, 0xF);
|
||||||
|
constexpr u32 Patch = PackCode(0x8, 0x0, 0xF);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
u32 min;
|
u32 min;
|
||||||
u32 max;
|
u32 max;
|
||||||
bool value_required = false;
|
bool value_required = false;
|
||||||
|
u32 panic;
|
||||||
|
|
||||||
Result check() {
|
Result check() {
|
||||||
if (!value_required && !value)
|
if (!value_required && !value)
|
||||||
@@ -143,22 +144,27 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
|
|
||||||
using namespace ams::ldr::hoc::pcv;
|
using namespace ams::ldr::hoc::pcv;
|
||||||
sValidator validators[] = {
|
sValidator validators[] = {
|
||||||
{ C.eristaCpuBoostClock, 1020'000, 2295'000, true },
|
{ C.eristaCpuBoostClock, 1020'000, 2295'000, true, panic::Cpu },
|
||||||
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true },
|
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true, panic::Cpu },
|
||||||
{ C.commonEmcMemVolt, 912'500, 1350'000 }, // Official burst vmax for the RAMs is 1500mV
|
{ C.eristaCpuMaxVolt, 1000, 1260, false, panic::Cpu },
|
||||||
{ C.eristaCpuMaxVolt, 1000, 1260 },
|
{ C.marikoCpuMaxVolt, 1000, 1200, false, panic::Cpu },
|
||||||
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000 },
|
{ eristaCpuDvfsMaxFreq, 1785'000, 2295'000, false, panic::Cpu },
|
||||||
{ C.marikoCpuMaxVolt, 1000, 1200 },
|
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000, false, panic::Cpu },
|
||||||
{ C.marikoEmcMaxClock, 1600'000, 3500'000 },
|
{ C.commonEmcMemVolt, 912'500, 1350'000, false, panic::Emc }, // Official burst vmax for the RAMs is 1500mV
|
||||||
{ C.marikoEmcVddqVolt, 250'000, 700'000 },
|
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000, false, panic::Emc },
|
||||||
{ eristaCpuDvfsMaxFreq, 1785'000, 2295'000 },
|
{ C.marikoEmcMaxClock, 1600'000, 3500'000, false, panic::Emc },
|
||||||
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000 },
|
{ C.marikoEmcVddqVolt, 250'000, 700'000, false, panic::Emc },
|
||||||
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000 },
|
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, false, panic::Gpu },
|
||||||
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000 },
|
{ marikoGpuDvfsMaxFreq, 768'000, 1570'000, false, panic::Gpu },
|
||||||
|
{ C.marikoGpuVmax, 800, 960, false, panic::Gpu },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& i : validators) {
|
for (auto &v : validators) {
|
||||||
if (R_FAILED(i.check())) {
|
if (R_FAILED(v.check())) {
|
||||||
|
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||||
|
panic::SmcError(v.panic);
|
||||||
|
#endif
|
||||||
|
|
||||||
CRASH("Validation FAIL");
|
CRASH("Validation FAIL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,8 +209,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
constexpr u32 CpuVoltL4T = 1257'000;
|
constexpr u32 CpuVoltL4T = 1257'000;
|
||||||
|
|
||||||
static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 };
|
static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 };
|
||||||
static const u32 cpuVoltDvfsOffsets[] = { 5, 6, 7, 8, 9 };
|
static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "invalid cpuVoltDvfsPattern size");
|
||||||
static_assert(sizeof(cpuVoltDvfsPattern) == sizeof(cpuVoltDvfsOffsets), "Invalid cpuVoltDvfsPattern");
|
|
||||||
|
|
||||||
static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 };
|
static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 };
|
||||||
static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "invalid cpuVoltageThermalPattern size");
|
static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "invalid cpuVoltageThermalPattern size");
|
||||||
@@ -285,6 +284,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constexpr u32 MemVoltHOS = 1125'000;
|
constexpr u32 MemVoltHOS = 1125'000;
|
||||||
|
constexpr u32 EmcClkMinFreq = 40800; /* 40.8 MHz table only exists on erista. */
|
||||||
constexpr u32 EmcClkPllmLimit = 1866'000'000;
|
constexpr u32 EmcClkPllmLimit = 1866'000'000;
|
||||||
|
|
||||||
constexpr u32 MTC_TABLE_REV = 7;
|
constexpr u32 MTC_TABLE_REV = 7;
|
||||||
|
|||||||
@@ -24,39 +24,24 @@
|
|||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::erista {
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
Result CpuVoltRange(u32* ptr) {
|
|
||||||
u32 min_volt_got = *(ptr - 1);
|
|
||||||
for (const auto& mv : CpuMinVolts) {
|
|
||||||
if (min_volt_got != mv)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!C.eristaCpuMaxVolt)
|
|
||||||
R_SKIP();
|
|
||||||
|
|
||||||
PATCH_OFFSET(ptr, C.eristaCpuMaxVolt);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CpuVoltDvfs(u32 *ptr) {
|
Result CpuVoltDvfs(u32 *ptr) {
|
||||||
if (MatchesPattern(ptr, cpuVoltDvfsPattern, cpuVoltDvfsOffsets)) {
|
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
|
||||||
if (C.eristaCpuVmin) {
|
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||||
PATCH_OFFSET(ptr, C.eristaCpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaCpuUV) {
|
|
||||||
PATCH_OFFSET(ptr - 2, C.eristaCpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaCpuMaxVolt) {
|
|
||||||
PATCH_OFFSET(ptr + 5, C.eristaCpuMaxVolt);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
if (C.eristaCpuVmin) {
|
||||||
|
PATCH_OFFSET(ptr, C.eristaCpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaCpuUV) {
|
||||||
|
PATCH_OFFSET(ptr - 2, C.eristaCpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaCpuMaxVolt) {
|
||||||
|
PATCH_OFFSET(ptr + 5, C.eristaCpuMaxVolt);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result CpuVoltThermals(u32 *ptr) {
|
Result CpuVoltThermals(u32 *ptr) {
|
||||||
@@ -67,7 +52,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
if (C.eristaCpuVmin) {
|
if (C.eristaCpuVmin) {
|
||||||
PATCH_OFFSET( ptr, C.eristaCpuVmin);
|
PATCH_OFFSET( ptr, C.eristaCpuVmin);
|
||||||
PATCH_OFFSET(ptr + 3, C.eristaCpuVmin);
|
PATCH_OFFSET(ptr + 3, C.eristaCpuVmin);
|
||||||
PATCH_OFFSET(ptr + 9, C.eristaCpuVmin);
|
PATCH_OFFSET(ptr + 6, C.eristaCpuVmin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (C.eristaCpuMaxVolt) {
|
if (C.eristaCpuMaxVolt) {
|
||||||
@@ -80,7 +65,6 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In theory this should work, but it doesn't, I have no idea why ¯\_(ツ)_/¯ */
|
|
||||||
Result CpuVoltDfll(u32* ptr) {
|
Result CpuVoltDfll(u32* ptr) {
|
||||||
cvb_cpu_dfll_data *entry = reinterpret_cast<cvb_cpu_dfll_data *>(ptr);
|
cvb_cpu_dfll_data *entry = reinterpret_cast<cvb_cpu_dfll_data *>(ptr);
|
||||||
R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
@@ -88,7 +72,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
|
||||||
if( !C.eristaCpuUV) {
|
if (!C.eristaCpuUV) {
|
||||||
R_SKIP();
|
R_SKIP();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,20 +104,19 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result GpuVoltDVFS(u32 *ptr) {
|
Result GpuVoltDVFS(u32 *ptr) {
|
||||||
u32 result = std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern));
|
if (std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern))) {
|
||||||
|
|
||||||
if (result)
|
|
||||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||||
|
}
|
||||||
|
|
||||||
if (C.eristaGpuVmin)
|
if (C.eristaGpuVmin) {
|
||||||
PATCH_OFFSET(ptr, C.eristaGpuVmin);
|
PATCH_OFFSET(ptr, C.eristaGpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GpuVoltThermals(u32 *ptr) {
|
Result GpuVoltThermals(u32 *ptr) {
|
||||||
u32 result = std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern));
|
if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) {
|
||||||
if (result) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,44 +354,54 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
table->min_volt = std::min(static_cast<u32>(1050), 900 + C.emcDvbShift * 25);
|
table->min_volt = std::min(static_cast<u32>(1050), 900 + C.emcDvbShift * 25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Probably more intuitive to point to 40800 rather than 1600000, but oh well. */
|
||||||
Result MemFreqMtcTable(u32 *ptr) {
|
Result MemFreqMtcTable(u32 *ptr) {
|
||||||
if (GET_MAX_OF_ARR(maxEmcClocks) <= EmcClkOSLimit) {
|
if (GET_MAX_OF_ARR(maxEmcClocks) <= EmcClkOSLimit) {
|
||||||
R_SKIP();
|
R_SKIP();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 khz_list[] = {1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800};
|
u32 khz_list[] = { 40800, 68000, 102000, 204000, 408000, 665600, 800000, 1065600, 1331200, 1600000 };
|
||||||
std::sort(maxEmcClocks, maxEmcClocks + std::size(maxEmcClocks), std::greater<>());
|
std::sort(maxEmcClocks, maxEmcClocks + std::size(maxEmcClocks));
|
||||||
u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
|
u32 khz_list_size = std::size(khz_list);
|
||||||
|
|
||||||
// Generate list for mtc table pointers
|
// Generate list for mtc table pointers
|
||||||
EristaMtcTable *table_list[khz_list_size];
|
EristaMtcTable *table_list[khz_list_size];
|
||||||
for (u32 i = 0; i < khz_list_size; i++) {
|
for (u32 i = 0; i < khz_list_size; i++) {
|
||||||
|
u32 mtcIndex = khz_list_size - 1 - i;
|
||||||
u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable);
|
u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable);
|
||||||
table_list[i] = reinterpret_cast<EristaMtcTable *>(table);
|
table_list[mtcIndex] = reinterpret_cast<EristaMtcTable *>(table);
|
||||||
R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
|
R_UNLESS(table_list[mtcIndex]->rate_khz == khz_list[mtcIndex], ldr::ResultInvalidMtcTable());
|
||||||
R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
|
R_UNLESS(table_list[mtcIndex]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 additionalFreqs = 0;
|
/* If we oc ram at all, tables are always shifted by at least 1. */
|
||||||
for (u32 i = 0; i < std::size(maxEmcClocks); ++i) {
|
u32 tableShifts = 1;
|
||||||
if (maxEmcClocks[i] > EmcClkOSLimit) {
|
for (u32 i = 0; i < std::size(maxEmcClocks) - 1; ++i) {
|
||||||
++additionalFreqs;
|
/* Duplicated mtc tables may cause pcv to not select frequencies properly, causing issues. */
|
||||||
|
if (maxEmcClocks[i] != maxEmcClocks[i + 1] && maxEmcClocks[i] > EmcClkOSLimit) {
|
||||||
|
++tableShifts;
|
||||||
} else {
|
} else {
|
||||||
break;
|
maxEmcClocks[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make room for new mtc table, discarding useless 40.8, 68000 and 102000 MHz table
|
/* Erista has extra, useless mtc tables, such as 40.8 Mhz, overwrite them to make room for oc freqs. */
|
||||||
// 40800 overwritten by 204000, ..., 1331200 overwritten by 1600000, leaving table_list[0], table_list[1] and table_list[2] not overwritten
|
/* More than 3 tables can be overwritten, but 3 is plenty. */
|
||||||
for (u32 i = khz_list_size - 1; i > additionalFreqs - 1; --i) {
|
std::memmove(table_list[0], table_list[tableShifts], sizeof(EristaMtcTable) * (khz_list_size - tableShifts));
|
||||||
std::memcpy(static_cast<void *>(table_list[i]), static_cast<void *>(table_list[i - additionalFreqs]), sizeof(EristaMtcTable));
|
|
||||||
|
/* Since we're not scaling r/w latency properly on Erista, we first overwrite the tables with the 1600 MHz table before scaling it. */
|
||||||
|
for (u32 i = 0; i < tableShifts; ++i) {
|
||||||
|
std::memcpy(table_list[khz_list_size - i - 1], table_list[khz_list_size - tableShifts - 1], sizeof(EristaMtcTable));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u32 i = 0; i < additionalFreqs; ++i) {
|
for (u32 i = tableShifts, j = 0; i > 0 && j < std::size(maxEmcClocks); ++j) {
|
||||||
/* Since we're not scaling latency timings properly, copy over the 1600Mhz table to get the closest timings. */
|
if (!maxEmcClocks[j]) {
|
||||||
std::memcpy(table_list[i], table_list[additionalFreqs], sizeof(EristaMtcTable));
|
continue;
|
||||||
table_list[i]->rate_khz = maxEmcClocks[i];
|
}
|
||||||
MemMtcTableAutoAdjust(table_list[i]);
|
|
||||||
|
table_list[khz_list_size - i]->rate_khz = maxEmcClocks[j];
|
||||||
|
MemMtcTableAutoAdjust(table_list[khz_list_size - i]);
|
||||||
|
--i;
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@@ -481,6 +474,10 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
for (auto &entry : patches) {
|
for (auto &entry : patches) {
|
||||||
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
||||||
if (R_FAILED(entry.CheckResult())) {
|
if (R_FAILED(entry.CheckResult())) {
|
||||||
|
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||||
|
panic::SmcError(panic::Patch);
|
||||||
|
#endif
|
||||||
|
|
||||||
CRASH(entry.description);
|
CRASH(entry.description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -415,6 +415,8 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
u32 trefbw = refresh_raw + 0x40;
|
u32 trefbw = refresh_raw + 0x40;
|
||||||
trefbw = MIN(trefbw, static_cast<u32>(0x3FFF));
|
trefbw = MIN(trefbw, static_cast<u32>(0x3FFF));
|
||||||
|
|
||||||
|
const u32 dyn_self_ref_control = (static_cast<u32>(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000);
|
||||||
|
|
||||||
CalculateTimings();
|
CalculateTimings();
|
||||||
|
|
||||||
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
|
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
|
||||||
@@ -446,7 +448,6 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
WRITE_PARAM_ALL_REG(table, emc_refresh, refresh_raw);
|
WRITE_PARAM_ALL_REG(table, emc_refresh, refresh_raw);
|
||||||
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, refresh_raw / 4);
|
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, refresh_raw / 4);
|
||||||
WRITE_PARAM_ALL_REG(table, emc_trefbw, trefbw);
|
WRITE_PARAM_ALL_REG(table, emc_trefbw, trefbw);
|
||||||
const u32 dyn_self_ref_control = (static_cast<u32>(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000);
|
|
||||||
WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, dyn_self_ref_control);
|
WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, dyn_self_ref_control);
|
||||||
WRITE_PARAM_ALL_REG(table, emc_pdex2wr, pdex2rw);
|
WRITE_PARAM_ALL_REG(table, emc_pdex2wr, pdex2rw);
|
||||||
WRITE_PARAM_ALL_REG(table, emc_pdex2rd, pdex2rw);
|
WRITE_PARAM_ALL_REG(table, emc_pdex2rd, pdex2rw);
|
||||||
@@ -479,8 +480,9 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, rdv);
|
WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, rdv);
|
||||||
WRITE_PARAM_ALL_REG(table, emc_rdv_mask, rdv + 2);
|
WRITE_PARAM_ALL_REG(table, emc_rdv_mask, rdv + 2);
|
||||||
WRITE_PARAM_ALL_REG(table, emc_tr_rdv, rdv);
|
WRITE_PARAM_ALL_REG(table, emc_tr_rdv, rdv);
|
||||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_2, 0x24)
|
/* TODO: Check this out again at some point. */
|
||||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_3, 0x24)
|
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_2, 0x24);
|
||||||
|
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_3, 0x24);
|
||||||
|
|
||||||
/* This needs some clean up. */
|
/* This needs some clean up. */
|
||||||
constexpr double MC_ARB_DIV = 4.0;
|
constexpr double MC_ARB_DIV = 4.0;
|
||||||
@@ -498,7 +500,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(tW2P / MC_ARB_DIV) + MC_ARB_SFA;
|
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(tW2P / MC_ARB_DIV) + MC_ARB_SFA;
|
||||||
|
|
||||||
/* Two consecutive reads between two different dram modules. */
|
/* Two consecutive reads between two different dram modules. */
|
||||||
/* Only be above 1 for 8gb ram. */
|
/* Only above 1 for 8gb ram. */
|
||||||
if (table->burst_mc_regs.mc_emem_arb_timing_r2r > 1) {
|
if (table->burst_mc_regs.mc_emem_arb_timing_r2r > 1) {
|
||||||
table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / 4) - 1 + MC_ARB_SFA;
|
table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / 4) - 1 + MC_ARB_SFA;
|
||||||
}
|
}
|
||||||
@@ -810,6 +812,10 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
for (auto &entry : patches) {
|
for (auto &entry : patches) {
|
||||||
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
||||||
if (R_FAILED(entry.CheckResult())) {
|
if (R_FAILED(entry.CheckResult())) {
|
||||||
|
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||||
|
panic::SmcError(panic::Patch);
|
||||||
|
#endif
|
||||||
|
|
||||||
CRASH(entry.description);
|
CRASH(entry.description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||||||
# NACP building is skipped as well.
|
# NACP building is skipped as well.
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
APP_TITLE := Horizon OC Monitor
|
APP_TITLE := Horizon OC Monitor
|
||||||
APP_VERSION := 1.3.2+r4-hoc
|
APP_VERSION := 1.3.2+r4-hoc-r2
|
||||||
TARGET := $(notdir $(CURDIR))
|
TARGET := $(notdir $(CURDIR))
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := source
|
SOURCES := source
|
||||||
|
|||||||
@@ -769,12 +769,27 @@ public:
|
|||||||
class FramePaddingConfig : public tsl::Gui {
|
class FramePaddingConfig : public tsl::Gui {
|
||||||
private:
|
private:
|
||||||
std::string modeName;
|
std::string modeName;
|
||||||
|
bool isMiniMode;
|
||||||
|
bool isGameResolutionsMode;
|
||||||
|
bool isFPSCounterMode;
|
||||||
|
bool isFPSGraphMode;
|
||||||
int currentPadding;
|
int currentPadding;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FramePaddingConfig(const std::string& mode) : modeName(mode) {
|
FramePaddingConfig(const std::string& mode) : modeName(mode) {
|
||||||
const std::string value = ult::parseValueFromIniSection(configIniPath, "mini", "frame_padding");
|
isMiniMode = (mode == "Mini");
|
||||||
currentPadding = value.empty() ? 10 : std::clamp(atoi(value.c_str()), 0, 14); // max value 14
|
isGameResolutionsMode = (mode == "Game Resolutions");
|
||||||
|
isFPSCounterMode = (mode == "FPS Counter");
|
||||||
|
isFPSGraphMode = (mode == "FPS Graph");
|
||||||
|
|
||||||
|
std::string section;
|
||||||
|
if (isMiniMode) section = "mini";
|
||||||
|
else if (isGameResolutionsMode) section = "game_resolutions";
|
||||||
|
else if (isFPSCounterMode) section = "fps-counter";
|
||||||
|
else if (isFPSGraphMode) section = "fps-graph";
|
||||||
|
|
||||||
|
const std::string value = ult::parseValueFromIniSection(configIniPath, section, "frame_padding");
|
||||||
|
currentPadding = value.empty() ? 10 : std::clamp(atoi(value.c_str()), 0, 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
~FramePaddingConfig() {
|
~FramePaddingConfig() {
|
||||||
@@ -785,6 +800,12 @@ public:
|
|||||||
auto* list = new tsl::elm::List();
|
auto* list = new tsl::elm::List();
|
||||||
list->addItem(new tsl::elm::CategoryHeader("Frame Padding"));
|
list->addItem(new tsl::elm::CategoryHeader("Frame Padding"));
|
||||||
|
|
||||||
|
std::string section;
|
||||||
|
if (isMiniMode) section = "mini";
|
||||||
|
else if (isGameResolutionsMode) section = "game_resolutions";
|
||||||
|
else if (isFPSCounterMode) section = "fps-counter";
|
||||||
|
else if (isFPSGraphMode) section = "fps-graph";
|
||||||
|
|
||||||
static const std::vector<int> paddingValues = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
|
static const std::vector<int> paddingValues = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
|
||||||
for (int padding : paddingValues) {
|
for (int padding : paddingValues) {
|
||||||
auto* paddingItem = new tsl::elm::ListItem(std::to_string(padding) + " px");
|
auto* paddingItem = new tsl::elm::ListItem(std::to_string(padding) + " px");
|
||||||
@@ -792,9 +813,9 @@ public:
|
|||||||
paddingItem->setValue(ult::CHECKMARK_SYMBOL);
|
paddingItem->setValue(ult::CHECKMARK_SYMBOL);
|
||||||
lastSelectedListItem = paddingItem;
|
lastSelectedListItem = paddingItem;
|
||||||
}
|
}
|
||||||
paddingItem->setClickListener([this, paddingItem, padding](uint64_t keys) {
|
paddingItem->setClickListener([this, paddingItem, padding, section](uint64_t keys) {
|
||||||
if (keys & KEY_A) {
|
if (keys & KEY_A) {
|
||||||
ult::setIniFileValue(configIniPath, "mini", "frame_padding", std::to_string(padding));
|
ult::setIniFileValue(configIniPath, section, "frame_padding", std::to_string(padding));
|
||||||
paddingItem->setValue(ult::CHECKMARK_SYMBOL);
|
paddingItem->setValue(ult::CHECKMARK_SYMBOL);
|
||||||
if (lastSelectedListItem && paddingItem != lastSelectedListItem)
|
if (lastSelectedListItem && paddingItem != lastSelectedListItem)
|
||||||
lastSelectedListItem->setValue("");
|
lastSelectedListItem->setValue("");
|
||||||
@@ -2084,11 +2105,15 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getCurrentFramePadding() {
|
int getCurrentFramePadding() {
|
||||||
if (isMiniMode) {
|
std::string section;
|
||||||
std::string value = ult::parseValueFromIniSection(configIniPath, "mini", "frame_padding");
|
if (isMiniMode) section = "mini";
|
||||||
return value.empty() ? 10 : atoi(value.c_str());
|
else if (isGameResolutionsMode) section = "game_resolutions";
|
||||||
}
|
else if (isFPSCounterMode) section = "fps-counter";
|
||||||
return 10;
|
else if (isFPSGraphMode) section = "fps-graph";
|
||||||
|
else return 10;
|
||||||
|
|
||||||
|
std::string value = ult::parseValueFromIniSection(configIniPath, section, "frame_padding");
|
||||||
|
return value.empty() ? 10 : atoi(value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getCurrentTextAlign() {
|
std::string getCurrentTextAlign() {
|
||||||
|
|||||||
@@ -79,14 +79,17 @@ Result sysclkIpcGetAPIVersion(u32* out_ver)
|
|||||||
Result sysclkIpcGetVersionString(char* out, size_t len)
|
Result sysclkIpcGetVersionString(char* out, size_t len)
|
||||||
{
|
{
|
||||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString,
|
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString,
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||||
.buffers = {{out, len}},
|
.buffers = {{out, len}},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
|
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
|
||||||
{
|
{
|
||||||
return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext, *out_context);
|
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext,
|
||||||
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||||
|
.buffers = {{out_context, sizeof(SysClkContext)}},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
|
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
|
||||||
@@ -112,7 +115,7 @@ Result sysclkIpcSetOverride(SysClkModule module, u32 hz)
|
|||||||
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
|
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
|
||||||
{
|
{
|
||||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid,
|
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid,
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||||
.buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}},
|
.buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -128,7 +131,7 @@ Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles)
|
|||||||
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
|
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
|
||||||
{
|
{
|
||||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues,
|
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues,
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||||
.buffers = {{out_configValues, sizeof(SysClkConfigValueList)}},
|
.buffers = {{out_configValues, sizeof(SysClkConfigValueList)}},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -136,7 +139,7 @@ Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
|
|||||||
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
|
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
|
||||||
{
|
{
|
||||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues,
|
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues,
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
|
||||||
.buffers = {{configValues, sizeof(SysClkConfigValueList)}},
|
.buffers = {{configValues, sizeof(SysClkConfigValueList)}},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
248
Source/sys-clk/common/include/SaltyNX.h
Normal file
248
Source/sys-clk/common/include/SaltyNX.h
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) MasaGratoR
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
|
Handle saltysd_orig;
|
||||||
|
|
||||||
|
Result SaltySD_Connect() {
|
||||||
|
for (int i = 0; i < 200; i++) {
|
||||||
|
if (!svcConnectToNamedPort(&saltysd_orig, "SaltySD"))
|
||||||
|
return 0;
|
||||||
|
svcSleepThread(1000*1000);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_Term()
|
||||||
|
{
|
||||||
|
Result ret;
|
||||||
|
IpcCommand c;
|
||||||
|
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input
|
||||||
|
{
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u64 zero;
|
||||||
|
u64 reserved[2];
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 0;
|
||||||
|
raw->zero = 0;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret))
|
||||||
|
{
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session terminated works too.
|
||||||
|
svcCloseHandle(saltysd_orig);
|
||||||
|
if (ret == 0xf601) return 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_CheckIfSharedMemoryAvailable(ptrdiff_t *offset, u64 size)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
// Send a command
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u64 size;
|
||||||
|
u32 reserved[2];
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 6;
|
||||||
|
raw->size = size;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u64 offset;
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
*offset = resp->offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_GetSharedMemoryHandle(Handle *retrieve)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
// Send a command
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u32 reserved[4];
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 7;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u64 reserved[2];
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
*retrieve = r.Handles[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_GetDisplayRefreshRate(uint8_t* refreshRate)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
// Send a command
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u64 zero;
|
||||||
|
u64 reserved;
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 10;
|
||||||
|
raw->zero = 0;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u64 refreshRate;
|
||||||
|
u64 reserved;
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
*refreshRate = (uint8_t)(resp->refreshRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaltySD_SetDisplayRefreshRate(uint8_t refreshRate)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
// Send a command
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
ipcSendPid(&c);
|
||||||
|
|
||||||
|
struct input {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
u64 refreshRate;
|
||||||
|
u64 reserved;
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 11;
|
||||||
|
raw->refreshRate = refreshRate;
|
||||||
|
|
||||||
|
ret = ipcDispatch(saltysd_orig);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ret)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u64 reserved[2];
|
||||||
|
} *resp = (output*)r.Raw;
|
||||||
|
|
||||||
|
ret = resp->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
// Battery charging flags
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BatteryFlag_NoHub = BIT(0), // Hub is disconnected
|
BatteryFlag_NoHub = BIT(0), // Hub is disconnected
|
||||||
BatteryFlag_Rail = BIT(8), // At least one Joy-con is charging from rail
|
BatteryFlag_Rail = BIT(8), // At least one Joy-con is charging from rail
|
||||||
@@ -27,7 +26,6 @@ typedef enum {
|
|||||||
BatteryFlag_ACC = BIT(16) // Accessory
|
BatteryFlag_ACC = BIT(16) // Accessory
|
||||||
} BatteryChargeFlags;
|
} BatteryChargeFlags;
|
||||||
|
|
||||||
// Power Delivery Controller State (BM92T series)
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PDState_NewPDO = 1, // Received new Power Data Object
|
PDState_NewPDO = 1, // Received new Power Data Object
|
||||||
PDState_NoPD = 2, // No Power Delivery source is detected
|
PDState_NoPD = 2, // No Power Delivery source is detected
|
||||||
@@ -47,14 +45,11 @@ typedef enum {
|
|||||||
ChargerType_Apple_1000mA = 8,
|
ChargerType_Apple_1000mA = 8,
|
||||||
ChargerType_Apple_2000mA = 9
|
ChargerType_Apple_2000mA = 9
|
||||||
} BatteryChargerType;
|
} BatteryChargerType;
|
||||||
|
|
||||||
// Power role (USB Power Delivery)
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PowerRole_Sink = 1, // Device is receiving power
|
PowerRole_Sink = 1, // Device is receiving power
|
||||||
PowerRole_Source = 2 // Device is providing power
|
PowerRole_Source = 2 // Device is providing power
|
||||||
} BatteryPowerRole;
|
} BatteryPowerRole;
|
||||||
|
|
||||||
// Complete battery charge information structure
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t InputCurrentLimit; // Input (Sink) current limit in mA
|
int32_t InputCurrentLimit; // Input (Sink) current limit in mA
|
||||||
int32_t VBUSCurrentLimit; // Output (Source/VBUS/OTG) current limit in mA
|
int32_t VBUSCurrentLimit; // Output (Source/VBUS/OTG) current limit in mA
|
||||||
@@ -64,7 +59,7 @@ typedef struct {
|
|||||||
int32_t unk_x14; // Unknown field (possibly flags)
|
int32_t unk_x14; // Unknown field (possibly flags)
|
||||||
BatteryPDControllerState PDControllerState; // PD Controller State
|
BatteryPDControllerState PDControllerState; // PD Controller State
|
||||||
int32_t BatteryTemperature; // Battery temperature in milli-Celsius
|
int32_t BatteryTemperature; // Battery temperature in milli-Celsius
|
||||||
int32_t RawBatteryCharge; // Battery charge in per cent-mille (100% = 100000)
|
int32_t RawBatteryCharge; // Battery charge in percentmille
|
||||||
int32_t VoltageAvg; // Average voltage in mV
|
int32_t VoltageAvg; // Average voltage in mV
|
||||||
int32_t BatteryAge; // Battery health (capacity full/design) in pcm
|
int32_t BatteryAge; // Battery health (capacity full/design) in pcm
|
||||||
BatteryPowerRole PowerRole; // Current power role
|
BatteryPowerRole PowerRole; // Current power role
|
||||||
@@ -74,36 +69,27 @@ typedef struct {
|
|||||||
BatteryChargeFlags Flags; // Various status flags
|
BatteryChargeFlags Flags; // Various status flags
|
||||||
} BatteryChargeInfo;
|
} BatteryChargeInfo;
|
||||||
|
|
||||||
// Helper macro to check if battery charging is enabled
|
|
||||||
#define IS_BATTERY_CHARGING_ENABLED(info) (((info)->unk_x14 >> 8) & 1)
|
#define IS_BATTERY_CHARGING_ENABLED(info) (((info)->unk_x14 >> 8) & 1)
|
||||||
|
|
||||||
// Initialize the battery info driver
|
|
||||||
Result batteryInfoInitialize(void);
|
Result batteryInfoInitialize(void);
|
||||||
|
|
||||||
// Cleanup the battery info driver
|
|
||||||
void batteryInfoExit(void);
|
void batteryInfoExit(void);
|
||||||
|
|
||||||
// Get complete battery charge information
|
|
||||||
Result batteryInfoGetChargeInfo(BatteryChargeInfo *out);
|
Result batteryInfoGetChargeInfo(BatteryChargeInfo *out);
|
||||||
|
|
||||||
// Get battery charge percentage (0-100)
|
|
||||||
Result batteryInfoGetChargePercentage(u32 *out);
|
Result batteryInfoGetChargePercentage(u32 *out);
|
||||||
|
|
||||||
// Check if enough power is being supplied
|
|
||||||
Result batteryInfoIsEnoughPowerSupplied(bool *out);
|
Result batteryInfoIsEnoughPowerSupplied(bool *out);
|
||||||
|
|
||||||
// Battery charge control functions
|
|
||||||
Result batteryInfoEnableCharging(void);
|
Result batteryInfoEnableCharging(void);
|
||||||
Result batteryInfoDisableCharging(void);
|
Result batteryInfoDisableCharging(void);
|
||||||
Result batteryInfoEnableFastCharging(void);
|
Result batteryInfoEnableFastCharging(void);
|
||||||
Result batteryInfoDisableFastCharging(void);
|
Result batteryInfoDisableFastCharging(void);
|
||||||
|
|
||||||
// Helper functions to get human-readable strings
|
|
||||||
const char* batteryInfoGetChargerTypeString(BatteryChargerType type);
|
const char* batteryInfoGetChargerTypeString(BatteryChargerType type);
|
||||||
const char* batteryInfoGetPowerRoleString(BatteryPowerRole role);
|
const char* batteryInfoGetPowerRoleString(BatteryPowerRole role);
|
||||||
const char* batteryInfoGetPDStateString(BatteryPDControllerState state);
|
const char* batteryInfoGetPDStateString(BatteryPDControllerState state);
|
||||||
|
|
||||||
// Convenience functions for common values
|
|
||||||
static inline int batteryInfoGetTemperatureMiliCelsius(BatteryChargeInfo *info) {
|
static inline int batteryInfoGetTemperatureMiliCelsius(BatteryChargeInfo *info) {
|
||||||
return info->BatteryTemperature;
|
return info->BatteryTemperature;
|
||||||
}
|
}
|
||||||
@@ -120,7 +106,6 @@ static inline bool batteryInfoIsCharging(BatteryChargeInfo *info) {
|
|||||||
return IS_BATTERY_CHARGING_ENABLED(info);
|
return IS_BATTERY_CHARGING_ENABLED(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// String lookup tables
|
|
||||||
static const char* s_chargerTypeStrings[] = {
|
static const char* s_chargerTypeStrings[] = {
|
||||||
"None",
|
"None",
|
||||||
"Power Delivery",
|
"Power Delivery",
|
||||||
@@ -187,7 +172,6 @@ static Result psmDisableFastBatteryCharging_internal(void) {
|
|||||||
return serviceDispatch(&g_psmService, 11);
|
return serviceDispatch(&g_psmService, 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public API implementations
|
|
||||||
Result batteryInfoInitialize(void) {
|
Result batteryInfoInitialize(void) {
|
||||||
if (g_batteryInfoInitialized)
|
if (g_batteryInfoInitialized)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -15,17 +15,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DISPLAY_REFRESH_RATE_H
|
#pragma once
|
||||||
#define DISPLAY_REFRESH_RATE_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Display mode structures
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t hFrontPorch;
|
uint16_t hFrontPorch;
|
||||||
uint8_t hSyncWidth;
|
uint8_t hSyncWidth;
|
||||||
@@ -52,7 +47,6 @@ typedef struct {
|
|||||||
uint8_t max;
|
uint8_t max;
|
||||||
} MinMaxRefreshRate;
|
} MinMaxRefreshRate;
|
||||||
|
|
||||||
// Display mode information
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t unk0;
|
uint32_t unk0;
|
||||||
uint32_t hActive;
|
uint32_t hActive;
|
||||||
@@ -76,7 +70,6 @@ typedef struct {
|
|||||||
uint32_t num_modes;
|
uint32_t num_modes;
|
||||||
} NvdcModeDB2;
|
} NvdcModeDB2;
|
||||||
|
|
||||||
// PLL structures
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned int PLLD_DIVM: 8;
|
unsigned int PLLD_DIVM: 8;
|
||||||
unsigned int reserved_1: 3;
|
unsigned int reserved_1: 3;
|
||||||
@@ -109,7 +102,6 @@ typedef struct {
|
|||||||
unsigned int reserved: 2;
|
unsigned int reserved: 2;
|
||||||
} PLLD_MISC;
|
} PLLD_MISC;
|
||||||
|
|
||||||
// Configuration structure
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t clkVirtAddr;
|
uint64_t clkVirtAddr;
|
||||||
uint64_t dsiVirtAddr;
|
uint64_t dsiVirtAddr;
|
||||||
@@ -132,9 +124,3 @@ uint8_t DisplayRefresh_GetDockedHighestAllowed(void);
|
|||||||
void DisplayRefresh_CorrectOledGamma(uint32_t refresh_rate);
|
void DisplayRefresh_CorrectOledGamma(uint32_t refresh_rate);
|
||||||
void DisplayRefresh_SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p);
|
void DisplayRefresh_SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p);
|
||||||
void DisplayRefresh_Shutdown(void);
|
void DisplayRefresh_Shutdown(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DISPLAY_REFRESH_RATE_H
|
|
||||||
756
Source/sys-clk/common/include/ipc.h
Normal file
756
Source/sys-clk/common/include/ipc.h
Normal file
@@ -0,0 +1,756 @@
|
|||||||
|
/**
|
||||||
|
* @file ipc.h
|
||||||
|
* @brief Inter-process communication handling
|
||||||
|
* @author plutoo
|
||||||
|
* @copyright libnx Authors (ISC License)
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
/// IPC input header magic
|
||||||
|
#define SFCI_MAGIC 0x49434653
|
||||||
|
/// IPC output header magic
|
||||||
|
#define SFCO_MAGIC 0x4f434653
|
||||||
|
|
||||||
|
/// IPC invalid object ID
|
||||||
|
#define IPC_INVALID_OBJECT_ID UINT32_MAX
|
||||||
|
|
||||||
|
///@name IPC request building
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/// IPC command (request) structure.
|
||||||
|
#define IPC_MAX_BUFFERS 8
|
||||||
|
#define IPC_MAX_OBJECTS 8
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BufferType_Normal=0, ///< Regular buffer.
|
||||||
|
BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory.
|
||||||
|
BufferType_Invalid=2,
|
||||||
|
BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping.
|
||||||
|
} BufferType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BufferDirection_Send=0,
|
||||||
|
BufferDirection_Recv=1,
|
||||||
|
BufferDirection_Exch=2,
|
||||||
|
} BufferDirection;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
IpcCommandType_Invalid = 0,
|
||||||
|
IpcCommandType_LegacyRequest = 1,
|
||||||
|
IpcCommandType_Close = 2,
|
||||||
|
IpcCommandType_LegacyControl = 3,
|
||||||
|
IpcCommandType_Request = 4,
|
||||||
|
IpcCommandType_Control = 5,
|
||||||
|
IpcCommandType_RequestWithContext = 6,
|
||||||
|
IpcCommandType_ControlWithContext = 7,
|
||||||
|
} IpcCommandType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DomainMessageType_Invalid = 0,
|
||||||
|
DomainMessageType_SendMessage = 1,
|
||||||
|
DomainMessageType_Close = 2,
|
||||||
|
} DomainMessageType;
|
||||||
|
|
||||||
|
/// IPC domain message header.
|
||||||
|
typedef struct {
|
||||||
|
u8 Type;
|
||||||
|
u8 NumObjectIds;
|
||||||
|
u16 Length;
|
||||||
|
u32 ThisObjectId;
|
||||||
|
u32 Pad[2];
|
||||||
|
} DomainMessageHeader;
|
||||||
|
|
||||||
|
/// IPC domain response header.
|
||||||
|
typedef struct {
|
||||||
|
u32 NumObjectIds;
|
||||||
|
u32 Pad[3];
|
||||||
|
} DomainResponseHeader;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t NumSend; // A
|
||||||
|
size_t NumRecv; // B
|
||||||
|
size_t NumExch; // W
|
||||||
|
const void* Buffers[IPC_MAX_BUFFERS];
|
||||||
|
size_t BufferSizes[IPC_MAX_BUFFERS];
|
||||||
|
BufferType BufferTypes[IPC_MAX_BUFFERS];
|
||||||
|
|
||||||
|
size_t NumStaticIn; // X
|
||||||
|
size_t NumStaticOut; // C
|
||||||
|
const void* Statics[IPC_MAX_BUFFERS];
|
||||||
|
size_t StaticSizes[IPC_MAX_BUFFERS];
|
||||||
|
u8 StaticIndices[IPC_MAX_BUFFERS];
|
||||||
|
|
||||||
|
bool SendPid;
|
||||||
|
size_t NumHandlesCopy;
|
||||||
|
size_t NumHandlesMove;
|
||||||
|
Handle Handles[IPC_MAX_OBJECTS];
|
||||||
|
|
||||||
|
size_t NumObjectIds;
|
||||||
|
u32 ObjectIds[IPC_MAX_OBJECTS];
|
||||||
|
} IpcCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
*/
|
||||||
|
static inline void ipcInitialize(IpcCommand* cmd) {
|
||||||
|
*cmd = (IpcCommand){};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IPC buffer descriptor.
|
||||||
|
typedef struct {
|
||||||
|
u32 Size; ///< Size of the buffer.
|
||||||
|
u32 Addr; ///< Lower 32-bits of the address of the buffer
|
||||||
|
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||||
|
} IpcBufferDescriptor;
|
||||||
|
|
||||||
|
/// IPC static send-buffer descriptor.
|
||||||
|
typedef struct {
|
||||||
|
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||||
|
u32 Addr; ///< Lower 32-bits of the address
|
||||||
|
} IpcStaticSendDescriptor;
|
||||||
|
|
||||||
|
/// IPC static receive-buffer descriptor.
|
||||||
|
typedef struct {
|
||||||
|
u32 Addr; ///< Lower 32-bits of the address of the buffer
|
||||||
|
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||||
|
} IpcStaticRecvDescriptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param type Buffer type.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type) {
|
||||||
|
size_t off = cmd->NumSend;
|
||||||
|
cmd->Buffers[off] = buffer;
|
||||||
|
cmd->BufferSizes[off] = size;
|
||||||
|
cmd->BufferTypes[off] = type;
|
||||||
|
cmd->NumSend++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a receive-buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param type Buffer type.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
|
||||||
|
size_t off = cmd->NumSend + cmd->NumRecv;
|
||||||
|
cmd->Buffers[off] = buffer;
|
||||||
|
cmd->BufferSizes[off] = size;
|
||||||
|
cmd->BufferTypes[off] = type;
|
||||||
|
cmd->NumRecv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds an exchange-buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param type Buffer type.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
|
||||||
|
size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch;
|
||||||
|
cmd->Buffers[off] = buffer;
|
||||||
|
cmd->BufferSizes[off] = size;
|
||||||
|
cmd->BufferTypes[off] = type;
|
||||||
|
cmd->NumExch++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a static-buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param index Index of buffer.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index) {
|
||||||
|
size_t off = cmd->NumStaticIn;
|
||||||
|
cmd->Statics[off] = buffer;
|
||||||
|
cmd->StaticSizes[off] = size;
|
||||||
|
cmd->StaticIndices[off] = index;
|
||||||
|
cmd->NumStaticIn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a static-receive-buffer to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param index Index of buffer.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index) {
|
||||||
|
size_t off = cmd->NumStaticIn + cmd->NumStaticOut;
|
||||||
|
cmd->Statics[off] = buffer;
|
||||||
|
cmd->StaticSizes[off] = size;
|
||||||
|
cmd->StaticIndices[off] = index;
|
||||||
|
cmd->NumStaticOut++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param pointer_buffer_size Pointer buffer size.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param index Index of buffer.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddSendSmart(IpcCommand* cmd, size_t pointer_buffer_size, const void* buffer, size_t size, u8 index) {
|
||||||
|
if (pointer_buffer_size != 0 && size <= pointer_buffer_size) {
|
||||||
|
ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal);
|
||||||
|
ipcAddSendStatic(cmd, buffer, size, index);
|
||||||
|
} else {
|
||||||
|
ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal);
|
||||||
|
ipcAddSendStatic(cmd, NULL, 0, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param pointer_buffer_size Pointer buffer size.
|
||||||
|
* @param buffer Address of the buffer.
|
||||||
|
* @param size Size of the buffer.
|
||||||
|
* @param index Index of buffer.
|
||||||
|
*/
|
||||||
|
static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t pointer_buffer_size, void* buffer, size_t size, u8 index) {
|
||||||
|
if (pointer_buffer_size != 0 && size <= pointer_buffer_size) {
|
||||||
|
ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal);
|
||||||
|
ipcAddRecvStatic(cmd, buffer, size, index);
|
||||||
|
} else {
|
||||||
|
ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal);
|
||||||
|
ipcAddRecvStatic(cmd, NULL, 0, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tags an IPC command structure to send the PID.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
*/
|
||||||
|
static inline void ipcSendPid(IpcCommand* cmd) {
|
||||||
|
cmd->SendPid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a copy-handle to be sent through an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param h Handle to send.
|
||||||
|
* @remark The receiving process gets a copy of the handle.
|
||||||
|
*/
|
||||||
|
static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h) {
|
||||||
|
cmd->Handles[cmd->NumHandlesCopy++] = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a move-handle to be sent through an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param h Handle to send.
|
||||||
|
* @remark The sending process loses ownership of the handle, which is transferred to the receiving process.
|
||||||
|
*/
|
||||||
|
static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h) {
|
||||||
|
cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepares the header of an IPC command structure.
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
|
||||||
|
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
|
||||||
|
*/
|
||||||
|
static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
size_t i;
|
||||||
|
*buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28);
|
||||||
|
|
||||||
|
u32* fill_in_size_later = buf;
|
||||||
|
|
||||||
|
if (cmd->NumStaticOut > 0) {
|
||||||
|
*buf = (cmd->NumStaticOut + 2) << 10;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) {
|
||||||
|
*buf++ |= 0x80000000;
|
||||||
|
*buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5);
|
||||||
|
|
||||||
|
if (cmd->SendPid)
|
||||||
|
buf += 2;
|
||||||
|
|
||||||
|
for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++)
|
||||||
|
*buf++ = cmd->Handles[i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<cmd->NumStaticIn; i++, buf+=2) {
|
||||||
|
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
|
||||||
|
|
||||||
|
uintptr_t ptr = (uintptr_t) cmd->Statics[i];
|
||||||
|
desc->Addr = ptr;
|
||||||
|
desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) |
|
||||||
|
(((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) {
|
||||||
|
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
|
||||||
|
desc->Size = cmd->BufferSizes[i];
|
||||||
|
|
||||||
|
uintptr_t ptr = (uintptr_t) cmd->Buffers[i];
|
||||||
|
desc->Addr = ptr;
|
||||||
|
desc->Packed = cmd->BufferTypes[i] |
|
||||||
|
(((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4;
|
||||||
|
u32* raw = (u32*) (buf + padding);
|
||||||
|
|
||||||
|
size_t raw_size = (sizeof_raw/4) + 4;
|
||||||
|
buf += raw_size;
|
||||||
|
|
||||||
|
u16* buf_u16 = (u16*) buf;
|
||||||
|
|
||||||
|
for (i=0; i<cmd->NumStaticOut; i++) {
|
||||||
|
size_t off = cmd->NumStaticIn + i;
|
||||||
|
size_t sz = (uintptr_t) cmd->StaticSizes[off];
|
||||||
|
|
||||||
|
buf_u16[i] = (sz > 0xFFFF) ? 0 : sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4;
|
||||||
|
buf += u16s_size;
|
||||||
|
raw_size += u16s_size;
|
||||||
|
|
||||||
|
*fill_in_size_later |= raw_size;
|
||||||
|
|
||||||
|
for (i=0; i<cmd->NumStaticOut; i++, buf+=2) {
|
||||||
|
IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf;
|
||||||
|
size_t off = cmd->NumStaticIn + i;
|
||||||
|
|
||||||
|
uintptr_t ptr = (uintptr_t) cmd->Statics[off];
|
||||||
|
desc->Addr = ptr;
|
||||||
|
desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void*) raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dispatches an IPC request.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcDispatch(Handle session) {
|
||||||
|
return svcSendSyncRequest(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
///@name IPC response parsing
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/// IPC parsed command (response) structure.
|
||||||
|
typedef struct {
|
||||||
|
IpcCommandType CommandType; ///< Type of the command
|
||||||
|
|
||||||
|
bool HasPid; ///< true if the 'Pid' field is filled out.
|
||||||
|
u64 Pid; ///< PID included in the response (only if HasPid is true)
|
||||||
|
|
||||||
|
size_t NumHandles; ///< Number of handles copied.
|
||||||
|
Handle Handles[IPC_MAX_OBJECTS]; ///< Handles.
|
||||||
|
bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied.
|
||||||
|
|
||||||
|
bool IsDomainRequest; ///< true if the the message is a Domain message.
|
||||||
|
DomainMessageType InMessageType; ///< Type of the domain message.
|
||||||
|
u32 InMessageLength; ///< Size of rawdata (for domain messages).
|
||||||
|
u32 InThisObjectId; ///< Object ID to call the command on (for domain messages).
|
||||||
|
size_t InNumObjectIds; ///< Number of object IDs (for domain messages).
|
||||||
|
u32 InObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages).
|
||||||
|
|
||||||
|
bool IsDomainResponse; ///< true if the the message is a Domain response.
|
||||||
|
size_t OutNumObjectIds; ///< Number of object IDs (for domain responses).
|
||||||
|
u32 OutObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain responses).
|
||||||
|
|
||||||
|
size_t NumBuffers; ///< Number of buffers in the response.
|
||||||
|
void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers.
|
||||||
|
size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers.
|
||||||
|
BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers.
|
||||||
|
BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer.
|
||||||
|
|
||||||
|
size_t NumStatics; ///< Number of statics in the response.
|
||||||
|
void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics.
|
||||||
|
size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics.
|
||||||
|
u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics.
|
||||||
|
|
||||||
|
size_t NumStaticsOut; ///< Number of output statics available in the response.
|
||||||
|
|
||||||
|
void* Raw; ///< Pointer to the raw embedded data structure in the response.
|
||||||
|
void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding.
|
||||||
|
size_t RawSize; ///< Size of the raw embedded data.
|
||||||
|
} IpcParsedCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse an IPC command response into an IPC parsed command structure.
|
||||||
|
* @param r IPC parsed command structure to fill in.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcParse(IpcParsedCommand* r) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
u32 ctrl0 = *buf++;
|
||||||
|
u32 ctrl1 = *buf++;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
r->IsDomainRequest = false;
|
||||||
|
r->IsDomainResponse = false;
|
||||||
|
|
||||||
|
r->CommandType = (IpcCommandType) (ctrl0 & 0xffff);
|
||||||
|
r->HasPid = false;
|
||||||
|
r->RawSize = (ctrl1 & 0x1ff) * 4;
|
||||||
|
r->NumHandles = 0;
|
||||||
|
|
||||||
|
r->NumStaticsOut = (ctrl1 >> 10) & 15;
|
||||||
|
if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor
|
||||||
|
if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors
|
||||||
|
|
||||||
|
if (ctrl1 & 0x80000000) {
|
||||||
|
u32 ctrl2 = *buf++;
|
||||||
|
|
||||||
|
if (ctrl2 & 1) {
|
||||||
|
r->HasPid = true;
|
||||||
|
r->Pid = *buf++;
|
||||||
|
r->Pid |= ((u64)(*buf++)) << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t num_handles_copy = ((ctrl2 >> 1) & 15);
|
||||||
|
size_t num_handles_move = ((ctrl2 >> 5) & 15);
|
||||||
|
|
||||||
|
size_t num_handles = num_handles_copy + num_handles_move;
|
||||||
|
u32* buf_after_handles = buf + num_handles;
|
||||||
|
|
||||||
|
if (num_handles > IPC_MAX_OBJECTS)
|
||||||
|
num_handles = IPC_MAX_OBJECTS;
|
||||||
|
|
||||||
|
for (i=0; i<num_handles; i++)
|
||||||
|
{
|
||||||
|
r->Handles[i] = *(buf+i);
|
||||||
|
r->WasHandleCopied[i] = (i < num_handles_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->NumHandles = num_handles;
|
||||||
|
buf = buf_after_handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t num_statics = (ctrl0 >> 16) & 15;
|
||||||
|
u32* buf_after_statics = buf + num_statics*2;
|
||||||
|
|
||||||
|
if (num_statics > IPC_MAX_BUFFERS)
|
||||||
|
num_statics = IPC_MAX_BUFFERS;
|
||||||
|
|
||||||
|
for (i=0; i<num_statics; i++, buf+=2) {
|
||||||
|
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
|
||||||
|
u64 packed = (u64) desc->Packed;
|
||||||
|
|
||||||
|
r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36));
|
||||||
|
r->StaticSizes[i] = packed >> 16;
|
||||||
|
r->StaticIndices[i] = packed & 63;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->NumStatics = num_statics;
|
||||||
|
buf = buf_after_statics;
|
||||||
|
|
||||||
|
size_t num_bufs_send = (ctrl0 >> 20) & 15;
|
||||||
|
size_t num_bufs_recv = (ctrl0 >> 24) & 15;
|
||||||
|
size_t num_bufs_exch = (ctrl0 >> 28) & 15;
|
||||||
|
|
||||||
|
size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch;
|
||||||
|
r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15);
|
||||||
|
r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3));
|
||||||
|
|
||||||
|
if (num_bufs > IPC_MAX_BUFFERS)
|
||||||
|
num_bufs = IPC_MAX_BUFFERS;
|
||||||
|
|
||||||
|
for (i=0; i<num_bufs; i++, buf+=3) {
|
||||||
|
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
|
||||||
|
u64 packed = (u64) desc->Packed;
|
||||||
|
|
||||||
|
r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36));
|
||||||
|
r->BufferSizes[i] = desc->Size;
|
||||||
|
r->BufferTypes[i] = (BufferType) (packed & 3);
|
||||||
|
|
||||||
|
if (i < num_bufs_send)
|
||||||
|
r->BufferDirections[i] = BufferDirection_Send;
|
||||||
|
else if (i < (num_bufs_send + num_bufs_recv))
|
||||||
|
r->BufferDirections[i] = BufferDirection_Recv;
|
||||||
|
else
|
||||||
|
r->BufferDirections[i] = BufferDirection_Exch;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->NumBuffers = num_bufs;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Queries the size of an IPC pointer buffer.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @param size Output variable in which to store the size.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
|
||||||
|
buf[0] = IpcCommandType_Control;
|
||||||
|
buf[1] = 8;
|
||||||
|
buf[2] = 0;
|
||||||
|
buf[3] = 0;
|
||||||
|
buf[4] = SFCI_MAGIC;
|
||||||
|
buf[5] = 0;
|
||||||
|
buf[6] = 3;
|
||||||
|
buf[7] = 0;
|
||||||
|
|
||||||
|
Result rc = ipcDispatch(session);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct ipcQueryPointerBufferSizeResponse {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u32 size;
|
||||||
|
} *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw;
|
||||||
|
|
||||||
|
rc = raw->result;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
*size = raw->size & 0xffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Closes the IPC session with proper clean up.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcCloseSession(Handle session) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
buf[0] = IpcCommandType_Close;
|
||||||
|
buf[1] = 0;
|
||||||
|
return ipcDispatch(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clones an IPC session.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @param unk Unknown.
|
||||||
|
* @param new_session_out Output cloned IPC session handle.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcCloneSession(Handle session, u32 unk, Handle* new_session_out) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
|
||||||
|
buf[0] = IpcCommandType_Control;
|
||||||
|
buf[1] = 9;
|
||||||
|
buf[2] = 0;
|
||||||
|
buf[3] = 0;
|
||||||
|
buf[4] = SFCI_MAGIC;
|
||||||
|
buf[5] = 0;
|
||||||
|
buf[6] = 4;
|
||||||
|
buf[7] = 0;
|
||||||
|
buf[8] = unk;
|
||||||
|
|
||||||
|
Result rc = ipcDispatch(session);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct ipcCloneSessionResponse {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
} *raw = (struct ipcCloneSessionResponse*)r.Raw;
|
||||||
|
|
||||||
|
rc = raw->result;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc) && new_session_out) {
|
||||||
|
*new_session_out = r.Handles[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
|
|
||||||
|
///@name IPC domain handling
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts an IPC session handle into a domain.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @param object_id_out Output variable in which to store the object ID.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) {
|
||||||
|
u32* buf = (u32*)armGetTls();
|
||||||
|
|
||||||
|
buf[0] = IpcCommandType_Control;
|
||||||
|
buf[1] = 8;
|
||||||
|
buf[4] = SFCI_MAGIC;
|
||||||
|
buf[5] = 0;
|
||||||
|
buf[6] = 0;
|
||||||
|
buf[7] = 0;
|
||||||
|
|
||||||
|
Result rc = ipcDispatch(session);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
IpcParsedCommand r;
|
||||||
|
ipcParse(&r);
|
||||||
|
|
||||||
|
struct ipcConvertSessionToDomainResponse {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
u32 object_id;
|
||||||
|
} *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw;
|
||||||
|
|
||||||
|
rc = raw->result;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
*object_id_out = raw->object_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds an object ID to be sent through an IPC domain command structure.
|
||||||
|
* @param cmd IPC domain command structure.
|
||||||
|
* @param object_id Object ID to send.
|
||||||
|
*/
|
||||||
|
static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id) {
|
||||||
|
cmd->ObjectIds[cmd->NumObjectIds++] = object_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepares the header of an IPC command structure (domain version).
|
||||||
|
* @param cmd IPC command structure.
|
||||||
|
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
|
||||||
|
* @param object_id Domain object ID.
|
||||||
|
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
|
||||||
|
*/
|
||||||
|
static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id) {
|
||||||
|
void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader) + cmd->NumObjectIds*sizeof(u32));
|
||||||
|
DomainMessageHeader* hdr = (DomainMessageHeader*) raw;
|
||||||
|
u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw);
|
||||||
|
|
||||||
|
hdr->Type = DomainMessageType_SendMessage;
|
||||||
|
hdr->NumObjectIds = (u8)cmd->NumObjectIds;
|
||||||
|
hdr->Length = sizeof_raw;
|
||||||
|
hdr->ThisObjectId = object_id;
|
||||||
|
hdr->Pad[0] = hdr->Pad[1] = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < cmd->NumObjectIds; i++)
|
||||||
|
object_ids[i] = cmd->ObjectIds[i];
|
||||||
|
return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse an IPC command request into an IPC parsed command structure (domain version).
|
||||||
|
* @param r IPC parsed command structure to fill in.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcParseDomainRequest(IpcParsedCommand* r) {
|
||||||
|
Result rc = ipcParse(r);
|
||||||
|
DomainMessageHeader *hdr;
|
||||||
|
u32 *object_ids;
|
||||||
|
if(R_FAILED(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hdr = (DomainMessageHeader*) r->Raw;
|
||||||
|
object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length);
|
||||||
|
r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader));
|
||||||
|
|
||||||
|
r->IsDomainRequest = true;
|
||||||
|
r->InMessageType = (DomainMessageType)(hdr->Type);
|
||||||
|
switch (r->InMessageType) {
|
||||||
|
case DomainMessageType_SendMessage:
|
||||||
|
case DomainMessageType_Close:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->InThisObjectId = hdr->ThisObjectId;
|
||||||
|
r->InNumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
|
||||||
|
if ((uintptr_t)object_ids + sizeof(u32) * r->InNumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < r->InNumObjectIds; i++)
|
||||||
|
r->InObjectIds[i] = object_ids[i];
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse an IPC command response into an IPC parsed command structure (domain version).
|
||||||
|
* @param r IPC parsed command structure to fill in.
|
||||||
|
* @param sizeof_raw Size in bytes of the raw data structure.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcParseDomainResponse(IpcParsedCommand* r, size_t sizeof_raw) {
|
||||||
|
Result rc = ipcParse(r);
|
||||||
|
DomainResponseHeader *hdr;
|
||||||
|
u32 *object_ids;
|
||||||
|
if(R_FAILED(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
hdr = (DomainResponseHeader*) r->Raw;
|
||||||
|
r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainResponseHeader));
|
||||||
|
object_ids = (u32*)(((uintptr_t) r->Raw) + sizeof_raw);//Official sw doesn't align this.
|
||||||
|
|
||||||
|
r->IsDomainResponse = true;
|
||||||
|
|
||||||
|
r->OutNumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
|
||||||
|
if ((uintptr_t)object_ids + sizeof(u32) * r->OutNumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
|
||||||
|
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < r->OutNumObjectIds; i++)
|
||||||
|
r->OutObjectIds[i] = object_ids[i];
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Closes a domain object by ID.
|
||||||
|
* @param session IPC session handle.
|
||||||
|
* @param object_id ID of the object to close.
|
||||||
|
* @return Result code.
|
||||||
|
*/
|
||||||
|
static inline Result ipcCloseObjectById(Handle session, u32 object_id) {
|
||||||
|
IpcCommand c;
|
||||||
|
DomainMessageHeader* hdr;
|
||||||
|
|
||||||
|
ipcInitialize(&c);
|
||||||
|
hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader));
|
||||||
|
|
||||||
|
hdr->Type = DomainMessageType_Close;
|
||||||
|
hdr->NumObjectIds = 0;
|
||||||
|
hdr->Length = 0;
|
||||||
|
hdr->ThisObjectId = object_id;
|
||||||
|
hdr->Pad[0] = hdr->Pad[1] = 0;
|
||||||
|
|
||||||
|
return ipcDispatch(session); // this command has no associated response
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
@@ -146,9 +146,13 @@ typedef enum {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
GovernorState_DoNotOverride = 0,
|
GovernorState_DoNotOverride = 0,
|
||||||
GovernorState_Disabled,
|
GovernorState_Disabled,
|
||||||
|
GovernorState_Enabled_CpuGpuVrr,
|
||||||
|
GovernorState_Enabled_CpuVrr,
|
||||||
|
GovernorState_Enabled_GpuVrr,
|
||||||
GovernorState_Enabled_CpuGpu,
|
GovernorState_Enabled_CpuGpu,
|
||||||
GovernorState_Enabled_Cpu,
|
GovernorState_Enabled_Cpu,
|
||||||
GovernorState_Enabled_Gpu,
|
GovernorState_Enabled_Gpu,
|
||||||
|
GovernorState_Enabled_Vrr,
|
||||||
GovernorState_EnumMax,
|
GovernorState_EnumMax,
|
||||||
} GovernorState;
|
} GovernorState;
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ typedef struct
|
|||||||
u16 iddq[HorizonOCSpeedo_EnumMax];
|
u16 iddq[HorizonOCSpeedo_EnumMax];
|
||||||
GpuSchedulingMode gpuSchedulingMode;
|
GpuSchedulingMode gpuSchedulingMode;
|
||||||
bool isSysDockInstalled;
|
bool isSysDockInstalled;
|
||||||
|
bool isSaltyNXInstalled;
|
||||||
u8 maxDisplayFreq;
|
u8 maxDisplayFreq;
|
||||||
u8 dramID;
|
u8 dramID;
|
||||||
bool isDram8GB;
|
bool isDram8GB;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ typedef enum {
|
|||||||
HorizonOCConfigValue_GPUSchedulingMethod,
|
HorizonOCConfigValue_GPUSchedulingMethod,
|
||||||
|
|
||||||
HorizonOCConfigValue_RAMVoltUsageDisplayMode,
|
HorizonOCConfigValue_RAMVoltUsageDisplayMode,
|
||||||
|
HorizonOCConfigValue_CpuGovernorMinimumFreq,
|
||||||
|
|
||||||
KipConfigValue_custRev,
|
KipConfigValue_custRev,
|
||||||
// KipConfigValue_mtcConf,
|
// KipConfigValue_mtcConf,
|
||||||
@@ -249,7 +250,8 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
|
|||||||
|
|
||||||
case HorizonOCConfigValue_RAMVoltUsageDisplayMode:
|
case HorizonOCConfigValue_RAMVoltUsageDisplayMode:
|
||||||
return pretty ? "RAM Voltage / Usage Display Mode" : "ram_volt_usage_display_mode";
|
return pretty ? "RAM Voltage / Usage Display Mode" : "ram_volt_usage_display_mode";
|
||||||
|
case HorizonOCConfigValue_CpuGovernorMinimumFreq:
|
||||||
|
return pretty ? "CPU Governor Minimum Frequency" : "cpu_gov_min_freq";
|
||||||
// KIP config values
|
// KIP config values
|
||||||
case KipConfigValue_custRev:
|
case KipConfigValue_custRev:
|
||||||
return pretty ? "Custom Revision" : "kip_cust_rev";
|
return pretty ? "Custom Revision" : "kip_cust_rev";
|
||||||
@@ -448,6 +450,8 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
|
|||||||
return 9600ULL; // 8600mW will trigger on erista stock, so raise it a bit
|
return 9600ULL; // 8600mW will trigger on erista stock, so raise it a bit
|
||||||
case HocClkConfigValue_LiteTDPLimit:
|
case HocClkConfigValue_LiteTDPLimit:
|
||||||
return 6400ULL; // 0.5C
|
return 6400ULL; // 0.5C
|
||||||
|
case HorizonOCConfigValue_CpuGovernorMinimumFreq:
|
||||||
|
return 612ULL; // 612MHz
|
||||||
default:
|
default:
|
||||||
return 0ULL;
|
return 0ULL;
|
||||||
}
|
}
|
||||||
@@ -579,6 +583,7 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
|
|||||||
case HorizonOCConfigValue_DVFSOffset:
|
case HorizonOCConfigValue_DVFSOffset:
|
||||||
case HorizonOCConfigValue_GPUScheduling:
|
case HorizonOCConfigValue_GPUScheduling:
|
||||||
case HorizonOCConfigValue_RAMVoltUsageDisplayMode:
|
case HorizonOCConfigValue_RAMVoltUsageDisplayMode:
|
||||||
|
case HorizonOCConfigValue_CpuGovernorMinimumFreq:
|
||||||
return true;
|
return true;
|
||||||
case HorizonOCConfigValue_BatteryChargeCurrent:
|
case HorizonOCConfigValue_BatteryChargeCurrent:
|
||||||
return ((input >= 1024) && (input <= 3072)) || !input;
|
return ((input >= 1024) && (input <= 3072)) || !input;
|
||||||
|
|||||||
@@ -79,14 +79,17 @@ Result sysclkIpcGetAPIVersion(u32* out_ver)
|
|||||||
Result sysclkIpcGetVersionString(char* out, size_t len)
|
Result sysclkIpcGetVersionString(char* out, size_t len)
|
||||||
{
|
{
|
||||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString,
|
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString,
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||||
.buffers = {{out, len}},
|
.buffers = {{out, len}},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
|
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
|
||||||
{
|
{
|
||||||
return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext, *out_context);
|
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext,
|
||||||
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||||
|
.buffers = {{out_context, sizeof(SysClkContext)}},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
|
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
|
||||||
@@ -112,7 +115,7 @@ Result sysclkIpcSetOverride(SysClkModule module, u32 hz)
|
|||||||
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
|
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
|
||||||
{
|
{
|
||||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid,
|
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid,
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||||
.buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}},
|
.buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -128,7 +131,7 @@ Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles)
|
|||||||
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
|
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
|
||||||
{
|
{
|
||||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues,
|
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues,
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||||
.buffers = {{out_configValues, sizeof(SysClkConfigValueList)}},
|
.buffers = {{out_configValues, sizeof(SysClkConfigValueList)}},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -136,7 +139,7 @@ Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
|
|||||||
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
|
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
|
||||||
{
|
{
|
||||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues,
|
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues,
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
|
||||||
.buffers = {{configValues, sizeof(SysClkConfigValueList)}},
|
.buffers = {{configValues, sizeof(SysClkConfigValueList)}},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,11 +29,9 @@
|
|||||||
|
|
||||||
#define MAX_REFRESH_RATE 72
|
#define MAX_REFRESH_RATE 72
|
||||||
|
|
||||||
// Configuration
|
|
||||||
static DisplayRefreshConfig g_config = {0};
|
static DisplayRefreshConfig g_config = {0};
|
||||||
static bool g_initialized = false;
|
static bool g_initialized = false;
|
||||||
|
|
||||||
// State
|
|
||||||
static uint8_t g_dockedHighestRefreshRate = 60;
|
static uint8_t g_dockedHighestRefreshRate = 60;
|
||||||
static uint8_t g_dockedLinkRate = 10;
|
static uint8_t g_dockedLinkRate = 10;
|
||||||
static bool g_wasRetroSuperTurnedOff = false;
|
static bool g_wasRetroSuperTurnedOff = false;
|
||||||
@@ -41,7 +39,6 @@ static uint32_t g_lastVActive = 1080;
|
|||||||
static bool g_canChangeRefreshRateDocked = false;
|
static bool g_canChangeRefreshRateDocked = false;
|
||||||
static uint8_t g_lastVActiveSet = 0;
|
static uint8_t g_lastVActiveSet = 0;
|
||||||
|
|
||||||
// Refresh rate tables
|
|
||||||
static const uint8_t g_dockedRefreshRates[] = {40, 45, 50, 55, 60, 70, 72, 75, 80, 90, 95, 100, 110, 120, 130, 140, 144, 150, 160, 165, 170, 180, 190, 200, 210, 220, 230, 240};
|
static const uint8_t g_dockedRefreshRates[] = {40, 45, 50, 55, 60, 70, 72, 75, 80, 90, 95, 100, 110, 120, 130, 140, 144, 150, 160, 165, 170, 180, 190, 200, 210, 220, 230, 240};
|
||||||
// Calculate with this tool:
|
// Calculate with this tool:
|
||||||
|
|
||||||
@@ -88,6 +85,7 @@ static const DockedTimings g_dockedTimings1080p[] = {
|
|||||||
{8, 32, 40, 108, 8, 6, 0, 528880}, //220Hz CVT-RBv2
|
{8, 32, 40, 108, 8, 6, 0, 528880}, //220Hz CVT-RBv2
|
||||||
{8, 32, 40, 114, 8, 6, 0, 555680}, //230Hz CVT-RBv2
|
{8, 32, 40, 114, 8, 6, 0, 555680}, //230Hz CVT-RBv2
|
||||||
{8, 32, 40, 121, 8, 6, 0, 583200}, //240Hz CVT-RBv2
|
{8, 32, 40, 121, 8, 6, 0, 583200}, //240Hz CVT-RBv2
|
||||||
|
// technically you can go to 476hz, but in practice, why would you?
|
||||||
};
|
};
|
||||||
|
|
||||||
static const HandheldTimings g_handheldTimingsRETRO[] = {
|
static const HandheldTimings g_handheldTimingsRETRO[] = {
|
||||||
@@ -100,7 +98,6 @@ static const HandheldTimings g_handheldTimingsRETRO[] = {
|
|||||||
|
|
||||||
static const MinMaxRefreshRate g_handheldModeRefreshRate = {40, 80};
|
static const MinMaxRefreshRate g_handheldModeRefreshRate = {40, 80};
|
||||||
|
|
||||||
// Utility functions
|
|
||||||
static uint8_t _getDockedRefreshRateIterator(uint32_t refreshRate) {
|
static uint8_t _getDockedRefreshRateIterator(uint32_t refreshRate) {
|
||||||
for (size_t i = 0; i < sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]); i++) {
|
for (size_t i = 0; i < sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]); i++) {
|
||||||
if (g_dockedRefreshRates[i] == refreshRate) return i;
|
if (g_dockedRefreshRates[i] == refreshRate) return i;
|
||||||
@@ -553,9 +550,10 @@ bool DisplayRefresh_SetRate(uint32_t new_refreshRate) {
|
|||||||
if (g_config.isRetroSUPER && !g_config.isDocked) {
|
if (g_config.isRetroSUPER && !g_config.isDocked) {
|
||||||
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
||||||
}
|
}
|
||||||
else if ((!g_config.isRetroSUPER && g_config.isLite) ||
|
|
||||||
nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
else if ((!g_config.isRetroSUPER && g_config.isLite) || R_FAILED(nvOpen(&fd, "/dev/nvdisp-disp1"))) {
|
||||||
return _setPLLDHandheldRefreshRate(new_refreshRate);
|
if (_setPLLDHandheldRefreshRate(new_refreshRate) == false)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
struct dpaux_read {
|
struct dpaux_read {
|
||||||
@@ -577,15 +575,19 @@ bool DisplayRefresh_SetRate(uint32_t new_refreshRate) {
|
|||||||
nvClose(fd);
|
nvClose(fd);
|
||||||
|
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
if (!g_config.isRetroSUPER) {
|
if (!g_config.isRetroSUPER) {
|
||||||
return _setPLLDHandheldRefreshRate(new_refreshRate);
|
return _setPLLDHandheldRefreshRate(new_refreshRate);
|
||||||
} else {
|
} else {
|
||||||
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return _setNvDispDockedRefreshRate(new_refreshRate);
|
if(g_config.isDocked)
|
||||||
|
return _setNvDispDockedRefreshRate(new_refreshRate);
|
||||||
|
else
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DisplayRefresh_GetRate(uint32_t* out_refreshRate, bool internal) {
|
bool DisplayRefresh_GetRate(uint32_t* out_refreshRate, bool internal) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk
|
|||||||
# version control constants
|
# version control constants
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
|
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
|
||||||
APP_VERSION := 0.42
|
APP_VERSION := 1.0.2
|
||||||
TARGET_VERSION := $(APP_VERSION)
|
TARGET_VERSION := $(APP_VERSION)
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|||||||
132
Source/sys-clk/overlay/lang/zh-tw.json
Normal file
132
Source/sys-clk/overlay/lang/zh-tw.json
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"Horizon OC Zeus": "Horizon OC Zeus",
|
||||||
|
"Edit App Profile": "編輯應用配置",
|
||||||
|
"Advanced": "高級",
|
||||||
|
"Edit Global Profile": "編輯全局配置",
|
||||||
|
"Temporary Overrides": "臨時覆蓋",
|
||||||
|
"Temporary Overrides Reset": "臨時覆蓋 重設",
|
||||||
|
"Settings": "設置",
|
||||||
|
"Information": "資訊",
|
||||||
|
"Enable": "啟用",
|
||||||
|
"Uncapped Clocks": "解除頻率上限",
|
||||||
|
"Override Boost Mode": "覆蓋加速模式",
|
||||||
|
"CPU Max Display Clock": "CPU 最大顯示頻率",
|
||||||
|
"Thermal Throttle": "溫度節流",
|
||||||
|
"Thermal Throttle Threshold": "溫度節流閾值",
|
||||||
|
"Handheld TDP": "掌機模式 TDP",
|
||||||
|
"Handheld TDP Limit": "掌機模式 TDP 限制",
|
||||||
|
"Lite TDP Limit": "Lite TDP 限制",
|
||||||
|
"Enforce Board Limit": "強制主板限制",
|
||||||
|
"Battery Charge Current": "電池充電電流",
|
||||||
|
"Display Refresh Rate Changing": "顯示刷新率變更",
|
||||||
|
"Fix CPU Volt Bug": "修復 CPU 電壓錯誤",
|
||||||
|
"[cfg] no enum format string": "[cfg] 無枚舉格式字串",
|
||||||
|
"KIP": "KIP",
|
||||||
|
"Save KIP Settings": "保存 KIP 設置",
|
||||||
|
"RAM Settings": "記憶體設定",
|
||||||
|
"CPU Settings": "CPU 設置",
|
||||||
|
"GPU Settings": "GPU 設置",
|
||||||
|
"Experimental": "實驗性功能",
|
||||||
|
"Charge Current Override": "充電電流覆蓋",
|
||||||
|
"Disabled": "禁用",
|
||||||
|
"HP Mode": "高性能模式",
|
||||||
|
"EMC Max Clock": "EMC 最大頻率",
|
||||||
|
"EMC VDD2 Voltage": "EMC VDD2 電壓",
|
||||||
|
"EMC VDDQ Voltage": "EMC VDDQ 電壓",
|
||||||
|
"DVB Shift": "DVB 偏移",
|
||||||
|
"Memory Timings": "記憶體時序",
|
||||||
|
"Memory Latencies": "記憶體延遲",
|
||||||
|
"t1 tRCD": "t1 tRCD",
|
||||||
|
"t2 tRP": "t2 tRP",
|
||||||
|
"t3 tRAS": "t3 tRAS",
|
||||||
|
"t4 tRRD": "t4 tRRD",
|
||||||
|
"t5 tRFC": "t5 tRFC",
|
||||||
|
"t6 tRTW": "t6 tRTW",
|
||||||
|
"t7 tWTR": "t7 tWTR",
|
||||||
|
"t8 tREFI": "t8 tREFI",
|
||||||
|
"Update RAM Timings": "更新記憶體時序",
|
||||||
|
"\uE150 This feature is EXPERIMENTAL": "\uE150 此功能為實驗性功能",
|
||||||
|
"and should only be used for testing!": "僅應用於測試!",
|
||||||
|
"Read Latency": "讀取延遲",
|
||||||
|
"Write Latency": "寫入延遲",
|
||||||
|
"CPU UV": "CPU 降壓",
|
||||||
|
"CPU Unlock": "CPU 解鎖",
|
||||||
|
"CPU VMIN": "CPU 最低電壓",
|
||||||
|
"CPU Max Voltage": "CPU 最大電壓",
|
||||||
|
"CPU UV Table": "CPU 降壓表",
|
||||||
|
"CPU Low UV": "CPU 低頻降壓",
|
||||||
|
"CPU High UV": "CPU 高頻降壓",
|
||||||
|
"CPU Max Clock": "CPU 最大頻率",
|
||||||
|
"CPU Low VMIN": "CPU 低頻最低電壓",
|
||||||
|
"CPU High VMIN": "CPU 高頻最低電壓",
|
||||||
|
"GPU Undervolt Table": "GPU 降壓表",
|
||||||
|
"Calculate GPU Vmin": "計算 GPU 最低電壓",
|
||||||
|
"GPU VMIN": "GPU 最低電壓",
|
||||||
|
"GPU VMAX": "GPU 最大電壓",
|
||||||
|
"GPU Volt Offset": "GPU 電壓偏移",
|
||||||
|
"GPU Custom Table": "GPU 自訂表",
|
||||||
|
"GPU Custom Table (mV)": "GPU 自訂表 (mV)",
|
||||||
|
"\uE150 Setting GPU Clocks past": "\uE150 將 GPU 頻率設置超過",
|
||||||
|
"1075MHz without UV, 1152MHz on SLT or ": "無降壓時的 1075MHz、SLT 時的 1152MHz 或",
|
||||||
|
"1228MHz on HiOPT can cause ": "HiOPT 時的 1228MHz 可能會造成",
|
||||||
|
"permanent damage to your Switch!": "對您的 Switch 造成永久性損壞!",
|
||||||
|
"Proceed at your own risk!": "風險自負!",
|
||||||
|
"921MHz without UV and 960MHz on": "無降壓時的 921MHz 和",
|
||||||
|
"SLT or HiOPT can cause ": "SLT 或 HiOPT 時的 960MHz 可能會造成",
|
||||||
|
"Auto": "自動",
|
||||||
|
"Sleep Mode": "休眠模式",
|
||||||
|
"Stock": "默認",
|
||||||
|
"Dev OC": "開發超頻",
|
||||||
|
"Boost Mode": "加速模式",
|
||||||
|
"Safe Max": "安全最大值",
|
||||||
|
"Unsafe Max": "不安全最大值",
|
||||||
|
"Absolute Max": "絕對最大值",
|
||||||
|
"Boost Mode & Safe Max": "加速模式 & 安全最大值",
|
||||||
|
"Official Rating": "官方額定值",
|
||||||
|
"Default (Mariko)": "默認 (Mariko)",
|
||||||
|
"Default (Erista)": "默認 (Erista)",
|
||||||
|
"Rating": "額定值",
|
||||||
|
"Safe Max (Mariko)": "安全最大值 (Mariko)",
|
||||||
|
"Safe Max (Erista)": "安全最大值 (Erista)",
|
||||||
|
"Default": "默認",
|
||||||
|
"1581MHz Tbreak": "1581MHz Tbreak",
|
||||||
|
"1683MHz Tbreak": "1683MHz Tbreak",
|
||||||
|
"Extreme UV Table": "極限降壓表",
|
||||||
|
"No UV": "不降壓",
|
||||||
|
"SLT Table": "SLT 表",
|
||||||
|
"HiOPT Table": "HiOPT 表",
|
||||||
|
"Power": "功耗",
|
||||||
|
"Temp": "溫度",
|
||||||
|
"Voltage": "電壓",
|
||||||
|
"TDP Threshold": "TDP 閾值",
|
||||||
|
"Lite TDP Threshold": "Lite TDP 閾值",
|
||||||
|
"Thermal Throttle Limit": "溫度節流限制",
|
||||||
|
"1600BL": "1600BL",
|
||||||
|
"1866BL": "1866BL",
|
||||||
|
"2133BL": "2133BL",
|
||||||
|
"BAT": "電池",
|
||||||
|
"FAN": "風扇",
|
||||||
|
"DISP": "顯示",
|
||||||
|
"Board": "主板",
|
||||||
|
"Skin": "外殼",
|
||||||
|
"Now": "當前",
|
||||||
|
"Avg": "平均",
|
||||||
|
"App ID": "應用 ID",
|
||||||
|
"Profile": "配置",
|
||||||
|
"CPU": "CPU",
|
||||||
|
"GPU": "GPU",
|
||||||
|
"Memory": "記憶體",
|
||||||
|
"Display": "顯示",
|
||||||
|
"Governor": "調速器",
|
||||||
|
"SOC": "SOC",
|
||||||
|
"PCB": "PCB",
|
||||||
|
"PMIC": "PMIC",
|
||||||
|
"Docked": "底座模式",
|
||||||
|
"Handheld": "掌機模式",
|
||||||
|
"Charging": "充電中",
|
||||||
|
"USB Charger": "USB 充電器",
|
||||||
|
"PD Charger": "PD 充電器",
|
||||||
|
"VDD2": "VDD2",
|
||||||
|
"VDDQ": "VDDQ",
|
||||||
|
"GPU DVFS": "GPU DVFS"
|
||||||
|
}
|
||||||
@@ -24,7 +24,10 @@
|
|||||||
|
|
||||||
tsl::elm::ListItem* SpeedoItem = NULL;
|
tsl::elm::ListItem* SpeedoItem = NULL;
|
||||||
tsl::elm::ListItem* IddqItem = NULL;
|
tsl::elm::ListItem* IddqItem = NULL;
|
||||||
|
tsl::elm::ListItem* DramModule = NULL;
|
||||||
tsl::elm::ListItem* sysdockStatusItem = NULL;
|
tsl::elm::ListItem* sysdockStatusItem = NULL;
|
||||||
|
tsl::elm::ListItem* saltyNXStatusItem = NULL;
|
||||||
|
|
||||||
ImageElement* CatImage = NULL;
|
ImageElement* CatImage = NULL;
|
||||||
HideableCategoryHeader* CatHeader = NULL;
|
HideableCategoryHeader* CatHeader = NULL;
|
||||||
HideableCustomDrawer* CatSpacer = NULL;
|
HideableCustomDrawer* CatSpacer = NULL;
|
||||||
@@ -53,10 +56,18 @@ void AboutGui::listUI()
|
|||||||
new tsl::elm::ListItem("IDDQ:");
|
new tsl::elm::ListItem("IDDQ:");
|
||||||
this->listElement->addItem(IddqItem);
|
this->listElement->addItem(IddqItem);
|
||||||
|
|
||||||
|
DramModule =
|
||||||
|
new tsl::elm::ListItem("Module: ");
|
||||||
|
this->listElement->addItem(DramModule);
|
||||||
|
|
||||||
sysdockStatusItem =
|
sysdockStatusItem =
|
||||||
new tsl::elm::ListItem("sys-dock status:");
|
new tsl::elm::ListItem("sys-dock status:");
|
||||||
this->listElement->addItem(sysdockStatusItem);
|
this->listElement->addItem(sysdockStatusItem);
|
||||||
|
|
||||||
|
saltyNXStatusItem =
|
||||||
|
new tsl::elm::ListItem("SaltyNX status:");
|
||||||
|
this->listElement->addItem(saltyNXStatusItem);
|
||||||
|
|
||||||
this->listElement->addItem(
|
this->listElement->addItem(
|
||||||
new tsl::elm::CategoryHeader("Credits")
|
new tsl::elm::CategoryHeader("Credits")
|
||||||
);
|
);
|
||||||
@@ -210,6 +221,49 @@ void AboutGui::listUI()
|
|||||||
this->listElement->addItem(CatSpacer);
|
this->listElement->addItem(CatSpacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string AboutGui::formatRamModule() {
|
||||||
|
switch (this->context->dramID) {
|
||||||
|
case 0: return "HB-MGCH 4GB";
|
||||||
|
case 4: return "HM-MGCH 6GB";
|
||||||
|
case 7: return "HM-MGXX 8GB";
|
||||||
|
|
||||||
|
case 1: return "NLE";
|
||||||
|
case 2: return "WT:C";
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
case 5 ... 6: return "NEE";
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
case 12: return "AM-MGCJ 4GB";
|
||||||
|
case 9:
|
||||||
|
case 13: return "AM-MGCJ 8GB";
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
case 14: return "NME";
|
||||||
|
|
||||||
|
case 11:
|
||||||
|
case 15: return "WT:E";
|
||||||
|
|
||||||
|
case 17:
|
||||||
|
case 19:
|
||||||
|
case 24: return "AA-MGCL 4GB";
|
||||||
|
|
||||||
|
case 18:
|
||||||
|
case 23:
|
||||||
|
case 28: return "AA-MGCL 8GB";
|
||||||
|
|
||||||
|
case 20 ... 22: return "AB-MGCL 4GB";
|
||||||
|
|
||||||
|
case 25 ... 27: return "WT:F";
|
||||||
|
|
||||||
|
case 29 ... 31: return "x267";
|
||||||
|
|
||||||
|
case 32 ... 34: return "WT:B";
|
||||||
|
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AboutGui::update()
|
void AboutGui::update()
|
||||||
{
|
{
|
||||||
BaseMenuGui::update();
|
BaseMenuGui::update();
|
||||||
@@ -218,6 +272,7 @@ void AboutGui::update()
|
|||||||
void AboutGui::refresh()
|
void AboutGui::refresh()
|
||||||
{
|
{
|
||||||
BaseMenuGui::refresh();
|
BaseMenuGui::refresh();
|
||||||
|
std::string ramModule = formatRamModule();
|
||||||
|
|
||||||
if (!this->context)
|
if (!this->context)
|
||||||
return;
|
return;
|
||||||
@@ -226,5 +281,7 @@ void AboutGui::refresh()
|
|||||||
sprintf(strings[1], "%u/%u/%u", this->context->iddq[HorizonOCSpeedo_CPU], this->context->iddq[HorizonOCSpeedo_GPU], this->context->iddq[HorizonOCSpeedo_SOC]);
|
sprintf(strings[1], "%u/%u/%u", this->context->iddq[HorizonOCSpeedo_CPU], this->context->iddq[HorizonOCSpeedo_GPU], this->context->iddq[HorizonOCSpeedo_SOC]);
|
||||||
SpeedoItem->setValue(strings[0]);
|
SpeedoItem->setValue(strings[0]);
|
||||||
IddqItem->setValue(strings[1]);
|
IddqItem->setValue(strings[1]);
|
||||||
|
DramModule->setValue(formatRamModule());
|
||||||
sysdockStatusItem->setValue(this->context->isSysDockInstalled ? "Installed" : "Not Installed");
|
sysdockStatusItem->setValue(this->context->isSysDockInstalled ? "Installed" : "Not Installed");
|
||||||
|
saltyNXStatusItem->setValue(this->context->isSaltyNXInstalled ? "Installed" : "Not Installed");
|
||||||
}
|
}
|
||||||
@@ -35,4 +35,7 @@ public:
|
|||||||
void listUI() override;
|
void listUI() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void refresh() override;
|
void refresh() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string formatRamModule();
|
||||||
};
|
};
|
||||||
@@ -292,7 +292,7 @@ void AppProfileGui::addProfileUI(SysClkProfile profile)
|
|||||||
this->addModuleListItem(profile, SysClkModule_MEM);
|
this->addModuleListItem(profile, SysClkModule_MEM);
|
||||||
#if IS_MINIMAL == 0
|
#if IS_MINIMAL == 0
|
||||||
ValueThresholds lcdThresholds(60, 65);
|
ValueThresholds lcdThresholds(60, 65);
|
||||||
if(!IsHoag() && configList.values[HorizonOCConfigValue_OverwriteRefreshRate]) {
|
if(configList.values[HorizonOCConfigValue_OverwriteRefreshRate]) {
|
||||||
if(profile != SysClkProfile_Docked) {
|
if(profile != SysClkProfile_Docked) {
|
||||||
this->addModuleListItemValue(profile, HorizonOCModule_Display, "Display", IsAula() ? 45 : 40, configList.values[HorizonOCConfigValue_EnableUnsafeDisplayFreqs] ? IsAula() ? 65 : 72 : 60, 1, " Hz", 1, 0, lcdThresholds);
|
this->addModuleListItemValue(profile, HorizonOCModule_Display, "Display", IsAula() ? 45 : 40, configList.values[HorizonOCConfigValue_EnableUnsafeDisplayFreqs] ? IsAula() ? 65 : 72 : 60, 1, " Hz", 1, 0, lcdThresholds);
|
||||||
} else {
|
} else {
|
||||||
@@ -360,13 +360,25 @@ void AppProfileGui::addProfileUI(SysClkProfile profile)
|
|||||||
NamedValue("115 Hz", 115),
|
NamedValue("115 Hz", 115),
|
||||||
NamedValue("120 Hz", 120)
|
NamedValue("120 Hz", 120)
|
||||||
};
|
};
|
||||||
|
if(configList.values[HorizonOCConfigValue_OverwriteRefreshRate] && !IsHoag())
|
||||||
this->addModuleListItemValue(profile, HorizonOCModule_Display, "Display", 50, 120, 1, " Hz", 1, 0, ValueThresholds(), dockedFreqsStandard);
|
this->addModuleListItemValue(profile, HorizonOCModule_Display, "Display", 50, 120, 1, " Hz", 1, 0, ValueThresholds(), dockedFreqsStandard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
std::vector<NamedValue> governorSettingsE = {
|
std::vector<NamedValue> governorSettingsE = {
|
||||||
|
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
||||||
|
NamedValue("Disabled", GovernorState_Disabled),
|
||||||
|
NamedValue("CPU + GPU + VRR", GovernorState_Enabled_CpuGpuVrr),
|
||||||
|
NamedValue("CPU + VRR", GovernorState_Enabled_CpuVrr),
|
||||||
|
NamedValue("GPU + VRR", GovernorState_Enabled_GpuVrr),
|
||||||
|
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
|
||||||
|
NamedValue("CPU", GovernorState_Enabled_Cpu),
|
||||||
|
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
||||||
|
NamedValue("VRR", GovernorState_Enabled_Vrr),
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<NamedValue> governorSettingsH = {
|
||||||
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
||||||
NamedValue("Disabled", GovernorState_Disabled),
|
NamedValue("Disabled", GovernorState_Disabled),
|
||||||
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
|
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
|
||||||
@@ -374,12 +386,7 @@ void AppProfileGui::addProfileUI(SysClkProfile profile)
|
|||||||
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<NamedValue> governorSettings = {
|
this->addModuleListItemValue(profile, HorizonOCModule_Governor, "Governor", 0, 0, 1, "", 1, 0, ValueThresholds(), IsHoag() ? governorSettingsH : governorSettingsE, false);
|
||||||
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
|
||||||
NamedValue("Disabled", GovernorState_Disabled),
|
|
||||||
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
|
||||||
};
|
|
||||||
this->addModuleListItemValue(profile, HorizonOCModule_Governor, "Governor", 0, 0, 1, "", 1, 0, ValueThresholds(), configList.values[HorizonOCConfigValue_EnableExperimentalSettings] ?governorSettingsE : governorSettings, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppProfileGui::listUI()
|
void AppProfileGui::listUI()
|
||||||
|
|||||||
@@ -48,16 +48,11 @@ std::string getVersionString() {
|
|||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------
|
static constexpr tsl::Color dynamicLogoRGB1 = tsl::Color(0, 4, 8, 15);
|
||||||
// AQUATIC BLUE COLORS (4-bit color space)
|
static constexpr tsl::Color dynamicLogoRGB2 = tsl::Color(7, 15, 15, 15);
|
||||||
// ---------------------------------------------
|
static constexpr tsl::Color STATIC_AQUA = tsl::Color(2, 10, 12, 15);
|
||||||
static constexpr tsl::Color dynamicLogoRGB1 = tsl::Color(0, 4, 8, 15); // Deep ocean blue
|
const std::string name = "Horizon OC Zeus";
|
||||||
static constexpr tsl::Color dynamicLogoRGB2 = tsl::Color(7, 15, 15, 15); // Bright aqua cyan
|
|
||||||
static constexpr tsl::Color STATIC_AQUA = tsl::Color(2, 10, 12, 15); // Mid aqua
|
|
||||||
|
|
||||||
// ---------------------------------------------
|
|
||||||
// FULLY ENHANCED ANIMATED LOGO EFFECT
|
|
||||||
// ---------------------------------------------
|
|
||||||
static s32 drawDynamicUltraText(
|
static s32 drawDynamicUltraText(
|
||||||
tsl::gfx::Renderer* renderer,
|
tsl::gfx::Renderer* renderer,
|
||||||
s32 startX,
|
s32 startX,
|
||||||
@@ -68,7 +63,6 @@ static s32 drawDynamicUltraText(
|
|||||||
{
|
{
|
||||||
static constexpr double cycleDuration = 1.6;
|
static constexpr double cycleDuration = 1.6;
|
||||||
|
|
||||||
const std::string name = "Horizon OC Zeus";
|
|
||||||
s32 currentX = startX;
|
s32 currentX = startX;
|
||||||
|
|
||||||
const u64 currentTime_ns = armTicksToNs(armGetSystemTick());
|
const u64 currentTime_ns = armTicksToNs(armGetSystemTick());
|
||||||
@@ -89,15 +83,9 @@ static s32 drawDynamicUltraText(
|
|||||||
double s1 = n * n * (3.0 - 2.0 * n);
|
double s1 = n * n * (3.0 - 2.0 * n);
|
||||||
double blend = std::clamp(s1, 0.0, 1.0);
|
double blend = std::clamp(s1, 0.0, 1.0);
|
||||||
|
|
||||||
// ---------------------------------------------
|
|
||||||
// Glow Pulse (brightness modulation)
|
|
||||||
// ---------------------------------------------
|
|
||||||
double glow = (cos(phase * 1.5) + 1.0) * 0.5;
|
double glow = (cos(phase * 1.5) + 1.0) * 0.5;
|
||||||
double brightness = 0.75 + glow * 0.25;
|
double brightness = 0.75 + glow * 0.25;
|
||||||
|
|
||||||
// ---------------------------------------------
|
|
||||||
// Color interpolation (4-bit!)
|
|
||||||
// ---------------------------------------------
|
|
||||||
u8 r = static_cast<u8>(
|
u8 r = static_cast<u8>(
|
||||||
(dynamicLogoRGB1.r + (dynamicLogoRGB2.r - dynamicLogoRGB1.r) * blend) * brightness
|
(dynamicLogoRGB1.r + (dynamicLogoRGB2.r - dynamicLogoRGB1.r) * blend) * brightness
|
||||||
);
|
);
|
||||||
@@ -112,21 +100,15 @@ static s32 drawDynamicUltraText(
|
|||||||
g = std::clamp<u8>(g, 0, 15);
|
g = std::clamp<u8>(g, 0, 15);
|
||||||
b = std::clamp<u8>(b, 0, 15);
|
b = std::clamp<u8>(b, 0, 15);
|
||||||
|
|
||||||
// ---------------------------------------------
|
|
||||||
// ZEUS Lightning Flash
|
|
||||||
// ---------------------------------------------
|
|
||||||
bool lightning = (fmod(timeNow, 5.0) < 0.15);
|
bool lightning = (fmod(timeNow, 5.0) < 0.15);
|
||||||
if (lightning) {
|
if (lightning) {
|
||||||
r = std::min<u8>(r + 4, 15);
|
r = std::min<u8>(r + 4, 15);
|
||||||
g = std::min<u8>(g + 4, 15);
|
g = std::min<u8>(g + 4, 15);
|
||||||
b = std::min<u8>(b + 15, 15); // strong blue spike
|
b = std::min<u8>(b + 15, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
tsl::Color color(r, g, b, 15);
|
tsl::Color color(r, g, b, 15);
|
||||||
|
|
||||||
// ---------------------------------------------
|
|
||||||
// Static Position (no vertical wobble)
|
|
||||||
// ---------------------------------------------
|
|
||||||
std::string ls(1, letter);
|
std::string ls(1, letter);
|
||||||
|
|
||||||
if (useNotificationMethod)
|
if (useNotificationMethod)
|
||||||
@@ -138,11 +120,7 @@ static s32 drawDynamicUltraText(
|
|||||||
return currentX;
|
return currentX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------
|
void BaseGui::preDraw(tsl::gfx::Renderer* renderer) {
|
||||||
// PRE-DRAW HOOK
|
|
||||||
// ---------------------------------------------
|
|
||||||
void BaseGui::preDraw(tsl::gfx::Renderer* renderer)
|
|
||||||
{
|
|
||||||
drawDynamicUltraText(
|
drawDynamicUltraText(
|
||||||
renderer,
|
renderer,
|
||||||
LOGO_X,
|
LOGO_X,
|
||||||
@@ -153,9 +131,6 @@ void BaseGui::preDraw(tsl::gfx::Renderer* renderer)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------
|
|
||||||
// UI SETUP
|
|
||||||
// ---------------------------------------------
|
|
||||||
tsl::elm::Element* BaseGui::createUI()
|
tsl::elm::Element* BaseGui::createUI()
|
||||||
{
|
{
|
||||||
BaseFrame* rootFrame = new BaseFrame(this);
|
BaseFrame* rootFrame = new BaseFrame(this);
|
||||||
@@ -163,9 +138,6 @@ tsl::elm::Element* BaseGui::createUI()
|
|||||||
return rootFrame;
|
return rootFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------
|
|
||||||
// LIVE UPDATE
|
|
||||||
// ---------------------------------------------
|
|
||||||
void BaseGui::update()
|
void BaseGui::update()
|
||||||
{
|
{
|
||||||
this->refresh();
|
this->refresh();
|
||||||
|
|||||||
@@ -58,7 +58,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", "DISP"
|
"App ID", "Profile", "CPU", "GPU", "MEM", "SoC", "Board", "Skin", "Now", "Avg", "BAT", "PMIC", "FAN", "DISP", "FPS"
|
||||||
};
|
};
|
||||||
|
|
||||||
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};
|
||||||
@@ -154,11 +154,11 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
|
|||||||
renderer->drawString(labels[10], false, positions[2], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
|
renderer->drawString(labels[10], false, positions[2], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
|
||||||
|
|
||||||
renderer->drawString(displayStrings[20], false, dataPositions[0], y, SMALL_TEXT_SIZE, tempColors[HorizonOCThermalSensor_Battery]); // Battery
|
renderer->drawString(displayStrings[20], false, dataPositions[0], y, SMALL_TEXT_SIZE, tempColors[HorizonOCThermalSensor_Battery]); // Battery
|
||||||
|
if(!IsHoag()) {
|
||||||
|
renderer->drawString(labels[13], false, positions[4], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // disp label
|
||||||
|
|
||||||
renderer->drawString(labels[13], false, positions[4], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // disp label
|
renderer->drawString(displayStrings[25], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // disp freq
|
||||||
|
}
|
||||||
renderer->drawString(displayStrings[25], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // disp freq
|
|
||||||
|
|
||||||
renderer->drawString(labels[12], false, positions[3], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // fan label
|
renderer->drawString(labels[12], false, positions[3], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // fan label
|
||||||
|
|
||||||
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
|
||||||
@@ -168,8 +168,10 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
|
|||||||
renderer->drawString(displayStrings[21], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Bat voltage
|
renderer->drawString(displayStrings[21], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Bat voltage
|
||||||
renderer->drawString(displayStrings[23], false, positions[2] - 2, y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Bat Age
|
renderer->drawString(displayStrings[23], false, positions[2] - 2, y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Bat Age
|
||||||
|
|
||||||
|
if(this->context->isSaltyNXInstalled) {
|
||||||
renderer->drawString(displayStrings[26], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // disp volt
|
renderer->drawString(labels[14], false, positions[4], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // FPS label
|
||||||
|
renderer->drawString(displayStrings[26], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // FPS
|
||||||
|
}
|
||||||
|
|
||||||
y+=20;
|
y+=20;
|
||||||
}
|
}
|
||||||
@@ -285,9 +287,14 @@ 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]);
|
sprintf(displayStrings[25], "%u Hz", context->realFreqs[HorizonOCModule_Display]);
|
||||||
|
if(this->context->isSaltyNXInstalled) {
|
||||||
//sprintf(displayStrings[26], "%u", context->speedos[HorizonOCSpeedo_CPU]);
|
if(context->fps == 254) {
|
||||||
|
strcpy(displayStrings[26], "N/A");
|
||||||
|
} else {
|
||||||
|
memset(displayStrings[26], 0, sizeof(displayStrings[26]));
|
||||||
|
sprintf(displayStrings[26], "%u", context->fps);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tsl::elm::Element* BaseMenuGui::baseUI()
|
tsl::elm::Element* BaseMenuGui::baseUI()
|
||||||
|
|||||||
@@ -290,6 +290,25 @@ void GlobalOverrideGui::addModuleToggleItem(SysClkModule module)
|
|||||||
this->listItems[module] = toggle;
|
this->listItems[module] = toggle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<NamedValue> governorSettingsE = {
|
||||||
|
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
||||||
|
NamedValue("Disabled", GovernorState_Disabled),
|
||||||
|
NamedValue("CPU + GPU + VRR", GovernorState_Enabled_CpuGpuVrr),
|
||||||
|
NamedValue("CPU + VRR", GovernorState_Enabled_CpuVrr),
|
||||||
|
NamedValue("GPU + VRR", GovernorState_Enabled_GpuVrr),
|
||||||
|
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
|
||||||
|
NamedValue("CPU", GovernorState_Enabled_Cpu),
|
||||||
|
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
||||||
|
NamedValue("VRR", GovernorState_Enabled_Vrr),
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<NamedValue> governorSettingsH = {
|
||||||
|
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
||||||
|
NamedValue("Disabled", GovernorState_Disabled),
|
||||||
|
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
|
||||||
|
NamedValue("CPU", GovernorState_Enabled_Cpu),
|
||||||
|
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
||||||
|
};
|
||||||
|
|
||||||
void GlobalOverrideGui::listUI()
|
void GlobalOverrideGui::listUI()
|
||||||
{
|
{
|
||||||
@@ -306,24 +325,11 @@ void GlobalOverrideGui::listUI()
|
|||||||
this->addModuleListItem(SysClkModule_MEM);
|
this->addModuleListItem(SysClkModule_MEM);
|
||||||
#if IS_MINIMAL == 0
|
#if IS_MINIMAL == 0
|
||||||
ValueThresholds lcdThresholds(60, 65);
|
ValueThresholds lcdThresholds(60, 65);
|
||||||
if(!IsHoag() && configList.values[HorizonOCConfigValue_OverwriteRefreshRate])
|
if(configList.values[HorizonOCConfigValue_OverwriteRefreshRate] && !IsHoag())
|
||||||
this->addModuleListItemValue(HorizonOCModule_Display, "Display", IsAula() ? 45 : 40, configList.values[HorizonOCConfigValue_EnableUnsafeDisplayFreqs] ? IsAula() ? 65 : 72 : 60, 1, " Hz", 1, 0, lcdThresholds);
|
this->addModuleListItemValue(HorizonOCModule_Display, "Display", IsAula() ? 45 : 40, configList.values[HorizonOCConfigValue_EnableUnsafeDisplayFreqs] ? IsAula() ? 65 : 72 : 60, 1, " Hz", 1, 0, lcdThresholds);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::vector<NamedValue> governorSettingsE = {
|
this->addModuleListItemValue(HorizonOCModule_Governor, "Governor", 0, 0, 1, "", 1, 0, ValueThresholds(), IsHoag() ? governorSettingsH : governorSettingsE, false);
|
||||||
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
|
||||||
NamedValue("Disabled", GovernorState_Disabled),
|
|
||||||
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
|
|
||||||
NamedValue("CPU", GovernorState_Enabled_Cpu),
|
|
||||||
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<NamedValue> governorSettings = {
|
|
||||||
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
|
||||||
NamedValue("Disabled", GovernorState_Disabled),
|
|
||||||
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
|
||||||
};
|
|
||||||
this->addModuleListItemValue(HorizonOCModule_Governor, "Governor", 0, 0, 1, "", 1, 0, ValueThresholds(), configList.values[HorizonOCConfigValue_EnableExperimentalSettings] ?governorSettingsE : governorSettings, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalOverrideGui::refresh()
|
void GlobalOverrideGui::refresh()
|
||||||
@@ -341,15 +347,8 @@ void GlobalOverrideGui::refresh()
|
|||||||
std::string displayText = FREQ_DEFAULT_TEXT;
|
std::string displayText = FREQ_DEFAULT_TEXT;
|
||||||
std::uint32_t currentValue = this->context->overrideFreqs[m];
|
std::uint32_t currentValue = this->context->overrideFreqs[m];
|
||||||
|
|
||||||
std::vector<NamedValue> governorSettings = {
|
|
||||||
NamedValue("Do Not Override", GovernorState_DoNotOverride),
|
|
||||||
NamedValue("Disabled", GovernorState_Disabled),
|
|
||||||
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
|
|
||||||
NamedValue("CPU", GovernorState_Enabled_Cpu),
|
|
||||||
NamedValue("GPU", GovernorState_Enabled_Gpu),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& setting : governorSettings) {
|
for (const auto& setting : governorSettingsE) {
|
||||||
if (setting.value == currentValue) {
|
if (setting.value == currentValue) {
|
||||||
displayText = setting.name;
|
displayText = setting.name;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -28,6 +28,10 @@
|
|||||||
#pragma message("Compiling with minimal features")
|
#pragma message("Compiling with minimal features")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define A_BTN "\ue0e0"
|
||||||
|
#define R_ARROW "\u2192"
|
||||||
|
class GeneralSettingsSubMenuGui;
|
||||||
|
class GovernorSettingsSubMenuGui;
|
||||||
class DisplaySubMenuGui;
|
class DisplaySubMenuGui;
|
||||||
class SafetySubMenuGui;
|
class SafetySubMenuGui;
|
||||||
class RamSubmenuGui;
|
class RamSubmenuGui;
|
||||||
@@ -37,6 +41,7 @@ class CpuSubmenuGui;
|
|||||||
class GpuSubmenuGui;
|
class GpuSubmenuGui;
|
||||||
class GpuCustomTableSubmenuGui;
|
class GpuCustomTableSubmenuGui;
|
||||||
class RamTableEditor;
|
class RamTableEditor;
|
||||||
|
|
||||||
MiscGui::MiscGui()
|
MiscGui::MiscGui()
|
||||||
{
|
{
|
||||||
this->configList = new SysClkConfigValueList {};
|
this->configList = new SysClkConfigValueList {};
|
||||||
@@ -357,14 +362,28 @@ void MiscGui::listUI()
|
|||||||
ValueThresholds thresholdsDisabled(0, 0);
|
ValueThresholds thresholdsDisabled(0, 0);
|
||||||
std::vector<NamedValue> noNamedValues = {};
|
std::vector<NamedValue> noNamedValues = {};
|
||||||
|
|
||||||
this->listElement->addItem(new tsl::elm::CategoryHeader("Hoc-clk Settings"));
|
this->listElement->addItem(new tsl::elm::CategoryHeader("Settings"));
|
||||||
std::vector<NamedValue> ramVoltDispModes = {
|
tsl::elm::ListItem* sysmoduleSettingsSubMenu = new tsl::elm::ListItem("General Settings");
|
||||||
NamedValue("VDD2 + VDDQ", RamDisplayMode_VDD2VDDQ),
|
sysmoduleSettingsSubMenu->setClickListener([](u64 keys) {
|
||||||
NamedValue("VDD2 + Usage", RamDisplayMode_VDD2Usage),
|
if (keys & HidNpadButton_A) {
|
||||||
NamedValue("VDDQ + Usage", RamDisplayMode_VDDQUsage),
|
tsl::changeTo<GeneralSettingsSubMenuGui>();
|
||||||
};
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
sysmoduleSettingsSubMenu->setValue(R_ARROW);
|
||||||
|
this->listElement->addItem(sysmoduleSettingsSubMenu);
|
||||||
|
|
||||||
addConfigButton(HorizonOCConfigValue_RAMVoltUsageDisplayMode, "RAM Voltage Display Mode", ValueRange(0, 12, 1, "", 0), "RAM Voltage Display Mode", &thresholdsDisabled, {}, ramVoltDispModes, false);
|
tsl::elm::ListItem* governorSettingsSubMenu = new tsl::elm::ListItem("Governor Settings");
|
||||||
|
governorSettingsSubMenu->setClickListener([](u64 keys) {
|
||||||
|
if (keys & HidNpadButton_A) {
|
||||||
|
tsl::changeTo<GovernorSettingsSubMenuGui>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
governorSettingsSubMenu->setValue(R_ARROW);
|
||||||
|
this->listElement->addItem(governorSettingsSubMenu);
|
||||||
|
|
||||||
tsl::elm::ListItem* safetySubmenu = new tsl::elm::ListItem("Safety Settings");
|
tsl::elm::ListItem* safetySubmenu = new tsl::elm::ListItem("Safety Settings");
|
||||||
safetySubmenu->setClickListener([](u64 keys) {
|
safetySubmenu->setClickListener([](u64 keys) {
|
||||||
@@ -374,9 +393,10 @@ void MiscGui::listUI()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
safetySubmenu->setValue(R_ARROW);
|
||||||
this->listElement->addItem(safetySubmenu);
|
this->listElement->addItem(safetySubmenu);
|
||||||
|
|
||||||
this->listElement->addItem(new tsl::elm::CategoryHeader("KIP"));
|
// this->listElement->addItem(new tsl::elm::CategoryHeader("KIP"));
|
||||||
|
|
||||||
tsl::elm::ListItem* saveBtn = new tsl::elm::ListItem("Save KIP Settings");
|
tsl::elm::ListItem* saveBtn = new tsl::elm::ListItem("Save KIP Settings");
|
||||||
saveBtn->setClickListener([](u64 keys) {
|
saveBtn->setClickListener([](u64 keys) {
|
||||||
@@ -390,6 +410,7 @@ void MiscGui::listUI()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
saveBtn->setValue(A_BTN);
|
||||||
this->listElement->addItem(saveBtn);
|
this->listElement->addItem(saveBtn);
|
||||||
|
|
||||||
tsl::elm::ListItem* ramSubmenu = new tsl::elm::ListItem("RAM Settings");
|
tsl::elm::ListItem* ramSubmenu = new tsl::elm::ListItem("RAM Settings");
|
||||||
@@ -400,6 +421,7 @@ void MiscGui::listUI()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
ramSubmenu->setValue(R_ARROW);
|
||||||
this->listElement->addItem(ramSubmenu);
|
this->listElement->addItem(ramSubmenu);
|
||||||
|
|
||||||
tsl::elm::ListItem* cpuSubmenu = new tsl::elm::ListItem("CPU Settings");
|
tsl::elm::ListItem* cpuSubmenu = new tsl::elm::ListItem("CPU Settings");
|
||||||
@@ -410,6 +432,7 @@ void MiscGui::listUI()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
cpuSubmenu->setValue(R_ARROW);
|
||||||
this->listElement->addItem(cpuSubmenu);
|
this->listElement->addItem(cpuSubmenu);
|
||||||
|
|
||||||
tsl::elm::ListItem* gpuSubmenu = new tsl::elm::ListItem("GPU Settings");
|
tsl::elm::ListItem* gpuSubmenu = new tsl::elm::ListItem("GPU Settings");
|
||||||
@@ -420,18 +443,20 @@ void MiscGui::listUI()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
gpuSubmenu->setValue(R_ARROW);
|
||||||
this->listElement->addItem(gpuSubmenu);
|
this->listElement->addItem(gpuSubmenu);
|
||||||
|
if(!IsHoag()) {
|
||||||
this->listElement->addItem(new tsl::elm::CategoryHeader("Display"));
|
tsl::elm::ListItem* displaySubMenu = new tsl::elm::ListItem("Display Settings");
|
||||||
tsl::elm::ListItem* displaySubMenu = new tsl::elm::ListItem("Display Settings");
|
displaySubMenu->setClickListener([](u64 keys) {
|
||||||
displaySubMenu->setClickListener([](u64 keys) {
|
if (keys & HidNpadButton_A) {
|
||||||
if (keys & HidNpadButton_A) {
|
tsl::changeTo<DisplaySubMenuGui>();
|
||||||
tsl::changeTo<DisplaySubMenuGui>();
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
return false;
|
||||||
return false;
|
});
|
||||||
});
|
displaySubMenu->setValue(R_ARROW);
|
||||||
this->listElement->addItem(displaySubMenu);
|
this->listElement->addItem(displaySubMenu);
|
||||||
|
}
|
||||||
|
|
||||||
#if IS_MINIMAL == 0
|
#if IS_MINIMAL == 0
|
||||||
// std::vector<NamedValue> chargerCurrents = {
|
// std::vector<NamedValue> chargerCurrents = {
|
||||||
@@ -529,24 +554,64 @@ void MiscGui::listUI()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GeneralSettingsSubMenuGui : public MiscGui {
|
||||||
|
public:
|
||||||
|
GeneralSettingsSubMenuGui() { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void listUI() override {
|
||||||
|
this->listElement->addItem(new tsl::elm::CategoryHeader("General Settings"));
|
||||||
|
ValueThresholds thresholdsDisabled(0, 0);
|
||||||
|
std::vector<NamedValue> ramVoltDispModes = {
|
||||||
|
NamedValue("VDD2 + VDDQ", RamDisplayMode_VDD2VDDQ),
|
||||||
|
NamedValue("VDD2 + Usage", RamDisplayMode_VDD2Usage),
|
||||||
|
NamedValue("VDDQ + Usage", RamDisplayMode_VDDQUsage),
|
||||||
|
};
|
||||||
|
|
||||||
|
addConfigButton(HorizonOCConfigValue_RAMVoltUsageDisplayMode, "RAM Voltage Display Mode", ValueRange(0, 12, 1, "", 0), "RAM Voltage Display Mode", &thresholdsDisabled, {}, ramVoltDispModes, false);
|
||||||
|
|
||||||
|
addConfigButton(
|
||||||
|
SysClkConfigValue_PollingIntervalMs,
|
||||||
|
"Polling Interval",
|
||||||
|
ValueRange(50, 1000, 50, "ms", 1),
|
||||||
|
"Polling Interval",
|
||||||
|
&thresholdsDisabled,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GovernorSettingsSubMenuGui : public MiscGui {
|
||||||
|
public:
|
||||||
|
GovernorSettingsSubMenuGui() { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void listUI() override {
|
||||||
|
this->listElement->addItem(new tsl::elm::CategoryHeader("Governor Settings"));
|
||||||
|
addFreqButton(HorizonOCConfigValue_CpuGovernorMinimumFreq, "CPU Governor Minimum Frequency", SysClkModule_CPU, BaseMenuGui::IsMariko() ? cpu_freq_label_m : cpu_freq_label_e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class DisplaySubMenuGui : public MiscGui {
|
class DisplaySubMenuGui : public MiscGui {
|
||||||
public:
|
public:
|
||||||
DisplaySubMenuGui() { }
|
DisplaySubMenuGui() { }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void listUI() override {
|
void listUI() override {
|
||||||
if(!IsHoag()) {
|
this->listElement->addItem(new tsl::elm::CategoryHeader("Display Settings"));
|
||||||
addConfigToggle(HorizonOCConfigValue_OverwriteRefreshRate, nullptr);
|
addConfigToggle(HorizonOCConfigValue_OverwriteRefreshRate, nullptr);
|
||||||
tsl::elm::CustomDrawer* warningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
|
tsl::elm::CustomDrawer* warningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
|
||||||
renderer->drawString("\uE150 Enabling unsafe display", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
|
renderer->drawString("\uE150 Enabling unsafe display", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
|
||||||
renderer->drawString("refresh rates may cause stress", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
|
renderer->drawString("refresh rates may cause stress", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
|
||||||
renderer->drawString("or damage to your display! ", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
|
renderer->drawString("or damage to your display! ", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
|
||||||
renderer->drawString("Proceed at your own risk!", false, x + 20, y + 90, 18, tsl::style::color::ColorText);
|
renderer->drawString("Proceed at your own risk!", false, x + 20, y + 90, 18, tsl::style::color::ColorText);
|
||||||
});
|
});
|
||||||
warningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 110);
|
warningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 110);
|
||||||
this->listElement->addItem(warningText);
|
this->listElement->addItem(warningText);
|
||||||
addConfigToggle(HorizonOCConfigValue_EnableUnsafeDisplayFreqs, nullptr);
|
addConfigToggle(HorizonOCConfigValue_EnableUnsafeDisplayFreqs, nullptr);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -657,15 +722,82 @@ protected:
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
tsl::elm::ListItem* freqSubmenu = new tsl::elm::ListItem("RAM Frequency Editor");
|
if (IsErista()) {
|
||||||
freqSubmenu->setClickListener([](u64 keys) {
|
tsl::elm::ListItem* freqSubmenu = new tsl::elm::ListItem("RAM Frequency Editor");
|
||||||
if (keys & HidNpadButton_A) {
|
freqSubmenu->setClickListener([](u64 keys) {
|
||||||
tsl::changeTo<RamTableEditor>();
|
if (keys & HidNpadButton_A) {
|
||||||
return true;
|
tsl::changeTo<RamTableEditor>();
|
||||||
}
|
return true;
|
||||||
return false;
|
}
|
||||||
});
|
return false;
|
||||||
this->listElement->addItem(freqSubmenu);
|
});
|
||||||
|
freqSubmenu->setValue(R_ARROW);
|
||||||
|
this->listElement->addItem(freqSubmenu);
|
||||||
|
} else {
|
||||||
|
std::vector<NamedValue> marikoMaxEmcClock = {
|
||||||
|
NamedValue("Disabled", 1600000),
|
||||||
|
NamedValue("1633 MHz", 1633000),
|
||||||
|
NamedValue("1666 MHz", 1666000),
|
||||||
|
NamedValue("1700 MHz", 1700000),
|
||||||
|
NamedValue("1733 MHz", 1733000),
|
||||||
|
NamedValue("1766 MHz", 1766000),
|
||||||
|
NamedValue("1800 MHz", 1800000),
|
||||||
|
NamedValue("1833 MHz", 1833000),
|
||||||
|
NamedValue("1866 MHz", 1866000, "JEDEC."),
|
||||||
|
NamedValue("1900 MHz", 1900000),
|
||||||
|
NamedValue("1933 MHz", 1933000),
|
||||||
|
NamedValue("1966 MHz", 1966000),
|
||||||
|
NamedValue("1996 MHz", 1996800, "JEDEC."),
|
||||||
|
NamedValue("2000 MHz", 2000000),
|
||||||
|
NamedValue("2033 MHz", 2033000),
|
||||||
|
NamedValue("2066 MHz", 2066000),
|
||||||
|
NamedValue("2100 MHz", 2100000),
|
||||||
|
NamedValue("2133 MHz", 2133000, "JEDEC."),
|
||||||
|
NamedValue("2166 MHz", 2166000),
|
||||||
|
NamedValue("2200 MHz", 2200000),
|
||||||
|
NamedValue("2233 MHz", 2233000),
|
||||||
|
NamedValue("2266 MHz", 2266000),
|
||||||
|
NamedValue("2300 MHz", 2300000),
|
||||||
|
NamedValue("2333 MHz", 2333000),
|
||||||
|
NamedValue("2366 MHz", 2366000),
|
||||||
|
NamedValue("2400 MHz", 2400000, "JEDEC."),
|
||||||
|
NamedValue("2433 MHz", 2433000),
|
||||||
|
NamedValue("2466 MHz", 2466000),
|
||||||
|
NamedValue("2500 MHz", 2500000),
|
||||||
|
NamedValue("2533 MHz", 2533000),
|
||||||
|
NamedValue("2566 MHz", 2566000),
|
||||||
|
NamedValue("2600 MHz", 2600000),
|
||||||
|
NamedValue("2633 MHz", 2633000),
|
||||||
|
NamedValue("2666 MHz", 2666000, "JEDEC."),
|
||||||
|
NamedValue("2700 MHz", 2700000),
|
||||||
|
NamedValue("2733 MHz", 2733000),
|
||||||
|
NamedValue("2766 MHz", 2766000),
|
||||||
|
NamedValue("2800 MHz", 2800000),
|
||||||
|
NamedValue("2833 MHz", 2833000),
|
||||||
|
NamedValue("2866 MHz", 2866000),
|
||||||
|
NamedValue("2900 MHz", 2900000),
|
||||||
|
NamedValue("2933 MHz", 2933000, "JEDEC."),
|
||||||
|
NamedValue("2966 MHz", 2966000),
|
||||||
|
NamedValue("3000 MHz", 3000000),
|
||||||
|
NamedValue("3033 MHz", 3033000),
|
||||||
|
NamedValue("3066 MHz", 3066000),
|
||||||
|
NamedValue("3100 MHz", 3100000),
|
||||||
|
NamedValue("3133 MHz", 3133000),
|
||||||
|
NamedValue("3166 MHz", 3166000),
|
||||||
|
NamedValue("3200 MHz", 3200000, "JEDEC."),
|
||||||
|
NamedValue("3233 MHz", 3233000, "High speedo needed!"),
|
||||||
|
NamedValue("3266 MHz", 3266000, "High speedo needed!"),
|
||||||
|
NamedValue("3300 MHz", 3300000, "High speedo needed!"),
|
||||||
|
// NamedValue("3333MHz (Needs extreme Speedo/PLL)", 3333000),
|
||||||
|
// NamedValue("3366MHz (Needs extreme Speedo/PLL)", 3366000),
|
||||||
|
// NamedValue("3400MHz (Needs extreme Speedo/PLL)", 3400000),
|
||||||
|
// NamedValue("3433MHz (Needs ridiculous Speedo/PLL)", 3433000),
|
||||||
|
// NamedValue("3466MHz (Needs ridiculous Speedo/PLL)", 3466000),
|
||||||
|
// NamedValue("3500MHz (Needs ridiculous Speedo/PLL)", 3500000),
|
||||||
|
};
|
||||||
|
|
||||||
|
addConfigButton(KipConfigValue_marikoEmcMaxClock, "Ram Max Clock", ValueRange(0, 1, 1, "", 1), "Ram Max Clock", &thresholdsDisabled, {}, marikoMaxEmcClock, false);
|
||||||
|
}
|
||||||
|
|
||||||
tsl::elm::ListItem* latenciesSubmenu = new tsl::elm::ListItem("RAM Latency Editor");
|
tsl::elm::ListItem* latenciesSubmenu = new tsl::elm::ListItem("RAM Latency Editor");
|
||||||
latenciesSubmenu->setClickListener([](u64 keys) {
|
latenciesSubmenu->setClickListener([](u64 keys) {
|
||||||
@@ -675,6 +807,7 @@ protected:
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
latenciesSubmenu->setValue(R_ARROW);
|
||||||
this->listElement->addItem(latenciesSubmenu);
|
this->listElement->addItem(latenciesSubmenu);
|
||||||
|
|
||||||
tsl::elm::ListItem* timingsSubmenu = new tsl::elm::ListItem("RAM Timing Reductions");
|
tsl::elm::ListItem* timingsSubmenu = new tsl::elm::ListItem("RAM Timing Reductions");
|
||||||
@@ -685,6 +818,7 @@ protected:
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
timingsSubmenu->setValue(R_ARROW);
|
||||||
this->listElement->addItem(timingsSubmenu);
|
this->listElement->addItem(timingsSubmenu);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -820,7 +954,7 @@ protected:
|
|||||||
NamedValue("2091 MHz", 2091000),
|
NamedValue("2091 MHz", 2091000),
|
||||||
NamedValue("2193 MHz", 2193000),
|
NamedValue("2193 MHz", 2193000),
|
||||||
NamedValue("2295 MHz", 2295000),
|
NamedValue("2295 MHz", 2295000),
|
||||||
NamedValue("2397 MHz", 2295000),
|
NamedValue("2397 MHz", 2397000),
|
||||||
};
|
};
|
||||||
ValueThresholds eCpuClockThresholds(1785000, 2091000);
|
ValueThresholds eCpuClockThresholds(1785000, 2091000);
|
||||||
addConfigButton(
|
addConfigButton(
|
||||||
@@ -850,7 +984,7 @@ protected:
|
|||||||
addConfigButton(
|
addConfigButton(
|
||||||
KipConfigValue_eristaCpuVmin,
|
KipConfigValue_eristaCpuVmin,
|
||||||
"CPU VMIN",
|
"CPU VMIN",
|
||||||
ValueRange(700, 900, 5, "mV", 1),
|
ValueRange(700, 900, 25, "mV", 1),
|
||||||
"CPU VMIN",
|
"CPU VMIN",
|
||||||
&thresholdsDisabled,
|
&thresholdsDisabled,
|
||||||
{},
|
{},
|
||||||
@@ -1003,146 +1137,68 @@ protected:
|
|||||||
this->listElement->addItem(new tsl::elm::CategoryHeader("RAM Frequency Editor"));
|
this->listElement->addItem(new tsl::elm::CategoryHeader("RAM Frequency Editor"));
|
||||||
|
|
||||||
ValueThresholds thresholdsDisabled(0, 0);
|
ValueThresholds thresholdsDisabled(0, 0);
|
||||||
if(IsMariko()) {
|
// 1600000, 1331200, 1065600, 800000, 665600, 408000, 204000
|
||||||
tsl::elm::ListItem* ramItem1600 = new tsl::elm::ListItem("1600 MHz");
|
|
||||||
this->listElement->addItem(ramItem1600);
|
|
||||||
|
|
||||||
std::vector<NamedValue> marikoMaxEmcClock = {
|
tsl::elm::ListItem* ramItem665 = new tsl::elm::ListItem("665 MHz");
|
||||||
NamedValue("Disabled", 1600000),
|
this->listElement->addItem(ramItem665);
|
||||||
NamedValue("1633 MHz", 1633000),
|
|
||||||
NamedValue("1666 MHz", 1666000),
|
|
||||||
NamedValue("1700 MHz", 1700000),
|
|
||||||
NamedValue("1733 MHz", 1733000),
|
|
||||||
NamedValue("1766 MHz", 1766000),
|
|
||||||
NamedValue("1800 MHz", 1800000),
|
|
||||||
NamedValue("1833 MHz", 1833000),
|
|
||||||
NamedValue("1866 MHz", 1866000, "JEDEC."),
|
|
||||||
NamedValue("1900 MHz", 1900000),
|
|
||||||
NamedValue("1933 MHz", 1933000),
|
|
||||||
NamedValue("1966 MHz", 1966000),
|
|
||||||
NamedValue("1996 MHz", 1996800, "JEDEC."),
|
|
||||||
NamedValue("2000 MHz", 2000000),
|
|
||||||
NamedValue("2033 MHz", 2033000),
|
|
||||||
NamedValue("2066 MHz", 2066000),
|
|
||||||
NamedValue("2100 MHz", 2100000),
|
|
||||||
NamedValue("2133 MHz", 2133000, "JEDEC."),
|
|
||||||
NamedValue("2166 MHz", 2166000),
|
|
||||||
NamedValue("2200 MHz", 2200000),
|
|
||||||
NamedValue("2233 MHz", 2233000),
|
|
||||||
NamedValue("2266 MHz", 2266000),
|
|
||||||
NamedValue("2300 MHz", 2300000),
|
|
||||||
NamedValue("2333 MHz", 2333000),
|
|
||||||
NamedValue("2366 MHz", 2366000),
|
|
||||||
NamedValue("2400 MHz", 2400000, "JEDEC."),
|
|
||||||
NamedValue("2433 MHz", 2433000),
|
|
||||||
NamedValue("2466 MHz", 2466000),
|
|
||||||
NamedValue("2500 MHz", 2500000),
|
|
||||||
NamedValue("2533 MHz", 2533000),
|
|
||||||
NamedValue("2566 MHz", 2566000),
|
|
||||||
NamedValue("2600 MHz", 2600000),
|
|
||||||
NamedValue("2633 MHz", 2633000),
|
|
||||||
NamedValue("2666 MHz", 2666000, "JEDEC."),
|
|
||||||
NamedValue("2700 MHz", 2700000),
|
|
||||||
NamedValue("2733 MHz", 2733000),
|
|
||||||
NamedValue("2766 MHz", 2766000),
|
|
||||||
NamedValue("2800 MHz", 2800000),
|
|
||||||
NamedValue("2833 MHz", 2833000),
|
|
||||||
NamedValue("2866 MHz", 2866000),
|
|
||||||
NamedValue("2900 MHz", 2900000),
|
|
||||||
NamedValue("2933 MHz", 2933000, "JEDEC."),
|
|
||||||
NamedValue("2966 MHz", 2966000),
|
|
||||||
NamedValue("3000 MHz", 3000000),
|
|
||||||
NamedValue("3033 MHz", 3033000),
|
|
||||||
NamedValue("3066 MHz", 3066000),
|
|
||||||
NamedValue("3100 MHz", 3100000),
|
|
||||||
NamedValue("3133 MHz", 3133000),
|
|
||||||
NamedValue("3166 MHz", 3166000),
|
|
||||||
NamedValue("3200 MHz", 3200000, "JEDEC."),
|
|
||||||
NamedValue("3233 MHz", 3233000, "High speedo needed!"),
|
|
||||||
NamedValue("3266 MHz", 3266000, "High speedo needed!"),
|
|
||||||
NamedValue("3300 MHz", 3300000, "High speedo needed!"),
|
|
||||||
// NamedValue("3333MHz (Needs extreme Speedo/PLL)", 3333000),
|
|
||||||
// NamedValue("3366MHz (Needs extreme Speedo/PLL)", 3366000),
|
|
||||||
// NamedValue("3400MHz (Needs extreme Speedo/PLL)", 3400000),
|
|
||||||
// NamedValue("3433MHz (Needs ridiculous Speedo/PLL)", 3433000),
|
|
||||||
// NamedValue("3466MHz (Needs ridiculous Speedo/PLL)", 3466000),
|
|
||||||
// NamedValue("3500MHz (Needs ridiculous Speedo/PLL)", 3500000),
|
|
||||||
};
|
|
||||||
addConfigButtonS(
|
|
||||||
KipConfigValue_marikoEmcMaxClock,
|
|
||||||
"",
|
|
||||||
ValueRange(0, 1, 1, "", 1),
|
|
||||||
"",
|
|
||||||
&thresholdsDisabled,
|
|
||||||
{},
|
|
||||||
marikoMaxEmcClock,
|
|
||||||
false,
|
|
||||||
"\ue0e0"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// 1600000, 1331200, 1065600, 800000, 665600, 408000, 204000
|
|
||||||
|
|
||||||
tsl::elm::ListItem* ramItem665 = new tsl::elm::ListItem("665 MHz");
|
tsl::elm::ListItem* ramItem800 = new tsl::elm::ListItem("800 MHz");
|
||||||
this->listElement->addItem(ramItem665);
|
this->listElement->addItem(ramItem800);
|
||||||
|
|
||||||
tsl::elm::ListItem* ramItem800 = new tsl::elm::ListItem("800 MHz");
|
tsl::elm::ListItem* ramItem1065 = new tsl::elm::ListItem("1065 MHz");
|
||||||
this->listElement->addItem(ramItem800);
|
this->listElement->addItem(ramItem1065);
|
||||||
|
|
||||||
tsl::elm::ListItem* ramItem1065 = new tsl::elm::ListItem("1065 MHz");
|
tsl::elm::ListItem* ramItem1331 = new tsl::elm::ListItem("1331 MHz");
|
||||||
this->listElement->addItem(ramItem1065);
|
this->listElement->addItem(ramItem1331);
|
||||||
|
|
||||||
tsl::elm::ListItem* ramItem1331 = new tsl::elm::ListItem("1331 MHz");
|
tsl::elm::ListItem* ramItem1600 = new tsl::elm::ListItem("1600 MHz");
|
||||||
this->listElement->addItem(ramItem1331);
|
this->listElement->addItem(ramItem1600);
|
||||||
|
|
||||||
tsl::elm::ListItem* ramItem1600 = new tsl::elm::ListItem("1600 MHz");
|
ValueThresholds eristaRamThresholds(2208000, 2304000);
|
||||||
this->listElement->addItem(ramItem1600);
|
|
||||||
|
|
||||||
ValueThresholds eristaRamThresholds(2208000, 2304000);
|
std::vector<NamedValue> eristaMaxEmcClock = {
|
||||||
|
NamedValue("Disabled", 1600000),
|
||||||
|
NamedValue("1633 MHz", 1633000),
|
||||||
|
NamedValue("1666 MHz", 1666000),
|
||||||
|
NamedValue("1700 MHz", 1700000),
|
||||||
|
NamedValue("1733 MHz", 1733000),
|
||||||
|
NamedValue("1766 MHz", 1766000),
|
||||||
|
NamedValue("1800 MHz", 1800000),
|
||||||
|
NamedValue("1833 MHz", 1833000),
|
||||||
|
NamedValue("1862 MHz", 1862400, "JEDEC."),
|
||||||
|
NamedValue("1881 MHz", 1881600),
|
||||||
|
NamedValue("1900 MHz", 1900800),
|
||||||
|
NamedValue("1920 MHz", 1920000),
|
||||||
|
NamedValue("1939 MHz", 1939200),
|
||||||
|
NamedValue("1958 MHz", 1958400),
|
||||||
|
NamedValue("1977 MHz", 1977600),
|
||||||
|
NamedValue("1996 MHz", 1996800, "JEDEC."),
|
||||||
|
NamedValue("2016 MHz", 2016000),
|
||||||
|
NamedValue("2035 MHz", 2035200),
|
||||||
|
NamedValue("2054 MHz", 2054400),
|
||||||
|
NamedValue("2073 MHz", 2073600),
|
||||||
|
NamedValue("2092 MHz", 2092800),
|
||||||
|
NamedValue("2112 MHz", 2112000),
|
||||||
|
NamedValue("2131 MHz", 2131200, "JEDEC."),
|
||||||
|
NamedValue("2150 MHz", 2150400),
|
||||||
|
NamedValue("2169 MHz", 2169600),
|
||||||
|
NamedValue("2188 MHz", 2188800),
|
||||||
|
NamedValue("2208 MHz", 2208000),
|
||||||
|
NamedValue("2227 MHz", 2227200),
|
||||||
|
NamedValue("2246 MHz", 2246400),
|
||||||
|
NamedValue("2265 MHz", 2265600),
|
||||||
|
NamedValue("2284 MHz", 2284800),
|
||||||
|
NamedValue("2304 MHz", 2304000),
|
||||||
|
NamedValue("2323 MHz", 2323200),
|
||||||
|
NamedValue("2342 MHz", 2342400),
|
||||||
|
NamedValue("2361 MHz", 2361600),
|
||||||
|
NamedValue("2380 MHz", 2380800),
|
||||||
|
NamedValue("2400 MHz", 2400000, "JEDEC."),
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<NamedValue> eristaMaxEmcClock = {
|
addConfigButtonS(KipConfigValue_eristaEmcMaxClock, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, A_BTN);
|
||||||
NamedValue("Disabled", 1600000),
|
addConfigButtonS(KipConfigValue_eristaEmcMaxClock1, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, A_BTN);
|
||||||
NamedValue("1633 MHz", 1633000),
|
addConfigButtonS(KipConfigValue_eristaEmcMaxClock2, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, A_BTN);
|
||||||
NamedValue("1666 MHz", 1666000),
|
|
||||||
NamedValue("1700 MHz", 1700000),
|
|
||||||
NamedValue("1733 MHz", 1733000),
|
|
||||||
NamedValue("1766 MHz", 1766000),
|
|
||||||
NamedValue("1800 MHz", 1800000),
|
|
||||||
NamedValue("1833 MHz", 1833000),
|
|
||||||
NamedValue("1862 MHz", 1862400, "JEDEC."),
|
|
||||||
NamedValue("1881 MHz", 1881600),
|
|
||||||
NamedValue("1900 MHz", 1900800),
|
|
||||||
NamedValue("1920 MHz", 1920000),
|
|
||||||
NamedValue("1939 MHz", 1939200),
|
|
||||||
NamedValue("1958 MHz", 1958400),
|
|
||||||
NamedValue("1977 MHz", 1977600),
|
|
||||||
NamedValue("1996 MHz", 1996800, "JEDEC."),
|
|
||||||
NamedValue("2016 MHz", 2016000),
|
|
||||||
NamedValue("2035 MHz", 2035200),
|
|
||||||
NamedValue("2054 MHz", 2054400),
|
|
||||||
NamedValue("2073 MHz", 2073600),
|
|
||||||
NamedValue("2092 MHz", 2092800),
|
|
||||||
NamedValue("2112 MHz", 2112000),
|
|
||||||
NamedValue("2131 MHz", 2131200, "JEDEC."),
|
|
||||||
NamedValue("2150 MHz", 2150400),
|
|
||||||
NamedValue("2169 MHz", 2169600),
|
|
||||||
NamedValue("2188 MHz", 2188800),
|
|
||||||
NamedValue("2208 MHz", 2208000),
|
|
||||||
NamedValue("2227 MHz", 2227200),
|
|
||||||
NamedValue("2246 MHz", 2246400),
|
|
||||||
NamedValue("2265 MHz", 2265600),
|
|
||||||
NamedValue("2284 MHz", 2284800),
|
|
||||||
NamedValue("2304 MHz", 2304000),
|
|
||||||
NamedValue("2323 MHz", 2323200),
|
|
||||||
NamedValue("2342 MHz", 2342400),
|
|
||||||
NamedValue("2361 MHz", 2361600),
|
|
||||||
NamedValue("2380 MHz", 2380800),
|
|
||||||
NamedValue("2400 MHz", 2400000, "JEDEC."),
|
|
||||||
};
|
|
||||||
|
|
||||||
addConfigButtonS(KipConfigValue_eristaEmcMaxClock, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, "\ue0e0");
|
|
||||||
addConfigButtonS(KipConfigValue_eristaEmcMaxClock1, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, "\ue0e0");
|
|
||||||
addConfigButtonS(KipConfigValue_eristaEmcMaxClock2, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, "\ue0e0");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1236,11 +1292,11 @@ protected:
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
addConfigButton(KipConfigValue_marikoGpuVmin, "GPU VMIN", ValueRange(0, 0, 0, "0", 1), "GPU VMIN", &thresholdsDisabled, {}, mGpuVoltsVmin, false);
|
addConfigButton(KipConfigValue_marikoGpuVmin, "GPU VMIN", ValueRange(0, 0, 0, "0", 1), "GPU VMIN", &thresholdsDisabled, {}, mGpuVoltsVmin, false);
|
||||||
ValueThresholds MgpuVmaxThresholds(800, 850);
|
ValueThresholds MgpuVmaxThresholds(805, 850);
|
||||||
addConfigButton(
|
addConfigButton(
|
||||||
KipConfigValue_marikoGpuVmax,
|
KipConfigValue_marikoGpuVmax,
|
||||||
"GPU Maximum Voltage",
|
"GPU Maximum Voltage",
|
||||||
ValueRange(750, 960, 5, "mV", 1),
|
ValueRange(800, 960, 5, "mV", 1),
|
||||||
"GPU Maximum Voltage",
|
"GPU Maximum Voltage",
|
||||||
&MgpuVmaxThresholds,
|
&MgpuVmaxThresholds,
|
||||||
{},
|
{},
|
||||||
@@ -1328,6 +1384,7 @@ protected:
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
customTableSubmenu->setValue(R_ARROW);
|
||||||
this->listElement->addItem(customTableSubmenu);
|
this->listElement->addItem(customTableSubmenu);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
"main_thread_stack_size": "0x0000C000",
|
"main_thread_stack_size": "0x0000C000",
|
||||||
"main_thread_priority": 16,
|
"main_thread_priority": 16,
|
||||||
"default_cpu_id": 3,
|
"default_cpu_id": 3,
|
||||||
"process_category": 0,
|
"process_category": 1,
|
||||||
"is_retail": true,
|
"is_retail": true,
|
||||||
"pool_partition": 2,
|
"pool_partition": 2,
|
||||||
"is_64_bit": true,
|
"is_64_bit": true,
|
||||||
"address_space_type": 1,
|
"address_space_type": 3,
|
||||||
"filesystem_access": {
|
"filesystem_access": {
|
||||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -276,11 +276,13 @@ void Board::Initialize()
|
|||||||
// threadStart(&cpuCore3Thread);
|
// threadStart(&cpuCore3Thread);
|
||||||
threadStart(&miscThread);
|
threadStart(&miscThread);
|
||||||
batteryInfoInitialize();
|
batteryInfoInitialize();
|
||||||
|
FetchHardwareInfos();
|
||||||
|
|
||||||
if (hosversionAtLeast(6,0,0) && R_SUCCEEDED(pwmInitialize())) {
|
if (hosversionAtLeast(6,0,0) && R_SUCCEEDED(pwmInitialize())) {
|
||||||
pwmCheck = pwmOpenSession2(&g_ICon, 0x3D000001);
|
pwmCheck = pwmOpenSession2(&g_ICon, 0x3D000001);
|
||||||
}
|
}
|
||||||
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag) {
|
|
||||||
|
if(!IsHoag()) {
|
||||||
u64 clkVirtAddr, dsiVirtAddr, outsize;
|
u64 clkVirtAddr, dsiVirtAddr, outsize;
|
||||||
rc = svcQueryMemoryMapping(&clkVirtAddr, &outsize, 0x60006000, 0x1000);
|
rc = svcQueryMemoryMapping(&clkVirtAddr, &outsize, 0x60006000, 0x1000);
|
||||||
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (clk)");
|
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (clk)");
|
||||||
@@ -292,9 +294,9 @@ void Board::Initialize()
|
|||||||
DisplayRefresh_Initialize(&cfg);
|
DisplayRefresh_Initialize(&cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
FetchHardwareInfos();
|
|
||||||
rc = svcQueryMemoryMapping(&cldvfs, &cldvfs_temp, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE);
|
rc = svcQueryMemoryMapping(&cldvfs, &cldvfs_temp, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE);
|
||||||
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (cldvfs)");
|
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (cldvfs)");
|
||||||
|
|
||||||
if(Board::GetSocType() == SysClkSocType_Erista) {
|
if(Board::GetSocType() == SysClkSocType_Erista) {
|
||||||
cachedEristaUvLowTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0);
|
cachedEristaUvLowTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0);
|
||||||
cachedEristaUvLowTune1 = *(u32*)(cldvfs + CL_DVFS_TUNE1_0);
|
cachedEristaUvLowTune1 = *(u32*)(cldvfs + CL_DVFS_TUNE1_0);
|
||||||
@@ -303,6 +305,8 @@ void Board::Initialize()
|
|||||||
cachedMarikoUvHighTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0);
|
cachedMarikoUvHighTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0);
|
||||||
Board::ResetToStockCpu();
|
Board::ResetToStockCpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Board::fuseReadSpeedos() {
|
void Board::fuseReadSpeedos() {
|
||||||
@@ -422,7 +426,7 @@ void Board::Exit()
|
|||||||
batteryInfoExit();
|
batteryInfoExit();
|
||||||
pmdmntExit();
|
pmdmntExit();
|
||||||
nvExit();
|
nvExit();
|
||||||
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag)
|
if(!IsHoag())
|
||||||
DisplayRefresh_Shutdown();
|
DisplayRefresh_Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,8 +461,9 @@ SysClkProfile Board::GetProfile()
|
|||||||
void Board::SetHz(SysClkModule module, std::uint32_t hz)
|
void Board::SetHz(SysClkModule module, std::uint32_t hz)
|
||||||
{
|
{
|
||||||
Result rc = 0;
|
Result rc = 0;
|
||||||
if(module == HorizonOCModule_Display && Board::GetConsoleType() != HorizonOCConsoleType_Hoag) {
|
if(module == HorizonOCModule_Display) {
|
||||||
DisplayRefresh_SetRate(hz);
|
if(!IsHoag())
|
||||||
|
DisplayRefresh_SetRate(hz);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(module > SysClkModule_MEM)
|
if(module > SysClkModule_MEM)
|
||||||
@@ -496,7 +501,7 @@ std::uint32_t Board::GetHz(SysClkModule module)
|
|||||||
std::uint32_t hz = 0;
|
std::uint32_t hz = 0;
|
||||||
|
|
||||||
if(module == HorizonOCModule_Display) {
|
if(module == HorizonOCModule_Display) {
|
||||||
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag)
|
if(!IsHoag())
|
||||||
DisplayRefresh_GetRate(&hz, false);
|
DisplayRefresh_GetRate(&hz, false);
|
||||||
else
|
else
|
||||||
hz = 60;
|
hz = 60;
|
||||||
@@ -536,7 +541,7 @@ std::uint32_t Board::GetRealHz(SysClkModule module)
|
|||||||
case SysClkModule_MEM:
|
case SysClkModule_MEM:
|
||||||
return t210ClkMemFreq();
|
return t210ClkMemFreq();
|
||||||
case HorizonOCModule_Display:
|
case HorizonOCModule_Display:
|
||||||
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag)
|
if(!IsHoag())
|
||||||
DisplayRefresh_GetRate(&hz, false);
|
DisplayRefresh_GetRate(&hz, false);
|
||||||
else
|
else
|
||||||
hz = 60;
|
hz = 60;
|
||||||
@@ -734,9 +739,8 @@ void Board::ResetToStockGpu()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Board::ResetToStockDisplay() {
|
void Board::ResetToStockDisplay() {
|
||||||
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag) {
|
if(!IsHoag())
|
||||||
DisplayRefresh_SetRate(60);
|
DisplayRefresh_SetRate(60);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 Board::GetHighestDockedDisplayRate() {
|
u8 Board::GetHighestDockedDisplayRate() {
|
||||||
@@ -1318,3 +1322,7 @@ u32 Board::CalculateTbreak(u32 table) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Board::IsHoag() {
|
||||||
|
return Board::GetConsoleType() == HorizonOCConsoleType_Hoag;
|
||||||
|
}
|
||||||
@@ -68,6 +68,7 @@ class Board
|
|||||||
static void SetDisplayRefreshDockedState(bool docked);
|
static void SetDisplayRefreshDockedState(bool docked);
|
||||||
static void SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint);
|
static void SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint);
|
||||||
static u32 CalculateTbreak(u32 table);
|
static u32 CalculateTbreak(u32 table);
|
||||||
|
static bool IsHoag();
|
||||||
protected:
|
protected:
|
||||||
static void FetchHardwareInfos();
|
static void FetchHardwareInfos();
|
||||||
static PcvModule GetPcvModule(SysClkModule sysclkModule);
|
static PcvModule GetPcvModule(SysClkModule sysclkModule);
|
||||||
|
|||||||
@@ -41,18 +41,26 @@
|
|||||||
#include <crc32.h>
|
#include <crc32.h>
|
||||||
|
|
||||||
#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0))
|
#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0))
|
||||||
|
|
||||||
|
// governor constants
|
||||||
|
#define POLL_NS 5'000'000 // 5 ms – governor poll rate
|
||||||
|
#define DOWN_HOLD_TICKS 10 // 50 ms – how long to in POLL_NS to hold while ramping down
|
||||||
|
#define STEP_UTIL 900 // multiplier for step calculations
|
||||||
|
|
||||||
bool isGpuGovernorEnabled = false;
|
bool isGpuGovernorEnabled = false;
|
||||||
bool isCpuGovernorEnabled = false;
|
bool isCpuGovernorEnabled = false;
|
||||||
bool lastGpuGovernorState = false;
|
bool lastGpuGovernorState = false;
|
||||||
bool lastCpuGovernorState = false;
|
bool lastCpuGovernorState = false;
|
||||||
|
bool lastVrrGovernorState = false;
|
||||||
bool hasChanged = true;
|
bool hasChanged = true;
|
||||||
ClockManager *ClockManager::instance = NULL;
|
ClockManager *ClockManager::instance = NULL;
|
||||||
Thread cpuGovernorTHREAD;
|
Thread cpuGovernorTHREAD;
|
||||||
Thread gpuGovernorTHREAD;
|
Thread gpuGovernorTHREAD;
|
||||||
|
Thread vrrTHREAD;
|
||||||
u32 initialConfigValues[SysClkConfigValue_EnumMax]; // initial config. used for safety checks
|
u32 initialConfigValues[SysClkConfigValue_EnumMax]; // initial config. used for safety checks
|
||||||
bool kipAvailable = false;
|
bool kipAvailable = false;
|
||||||
bool isCpuGovernorInBoostMode = false;
|
bool isCpuGovernorInBoostMode = false;
|
||||||
|
bool isVRREnabled = false;
|
||||||
ClockManager *ClockManager::GetInstance()
|
ClockManager *ClockManager::GetInstance()
|
||||||
{
|
{
|
||||||
return instance;
|
return instance;
|
||||||
@@ -93,6 +101,8 @@ ClockManager::ClockManager()
|
|||||||
this->lastCsvWriteNs = 0;
|
this->lastCsvWriteNs = 0;
|
||||||
|
|
||||||
this->sysDockIntegration = new SysDockIntegration;
|
this->sysDockIntegration = new SysDockIntegration;
|
||||||
|
this->saltyNXIntegration = new SaltyNXIntegration;
|
||||||
|
|
||||||
memset(&initialConfigValues, 0, sizeof(initialConfigValues));
|
memset(&initialConfigValues, 0, sizeof(initialConfigValues));
|
||||||
this->GetKipData();
|
this->GetKipData();
|
||||||
|
|
||||||
@@ -116,8 +126,15 @@ ClockManager::ClockManager()
|
|||||||
-2
|
-2
|
||||||
);
|
);
|
||||||
|
|
||||||
threadStart(&cpuGovernorTHREAD);
|
threadCreate(
|
||||||
threadStart(&gpuGovernorTHREAD);
|
&vrrTHREAD,
|
||||||
|
ClockManager::VRRThread,
|
||||||
|
this,
|
||||||
|
NULL,
|
||||||
|
0x2000,
|
||||||
|
0x3F,
|
||||||
|
-2
|
||||||
|
);
|
||||||
|
|
||||||
for(int i = 0; i < HorizonOCSpeedo_EnumMax; i++) {
|
for(int i = 0; i < HorizonOCSpeedo_EnumMax; i++) {
|
||||||
this->context->speedos[i] = Board::getSpeedo((HorizonOCSpeedo)i);
|
this->context->speedos[i] = Board::getSpeedo((HorizonOCSpeedo)i);
|
||||||
@@ -128,13 +145,27 @@ ClockManager::ClockManager()
|
|||||||
this->context->isDram8GB = Board::IsDram8GB();
|
this->context->isDram8GB = Board::IsDram8GB();
|
||||||
Board::SetGpuSchedulingMode((GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling), (GpuSchedulingOverrideMethod)this->config->GetConfigValue(HorizonOCConfigValue_GPUSchedulingMethod));
|
Board::SetGpuSchedulingMode((GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling), (GpuSchedulingOverrideMethod)this->config->GetConfigValue(HorizonOCConfigValue_GPUSchedulingMethod));
|
||||||
this->context->gpuSchedulingMode = (GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling);
|
this->context->gpuSchedulingMode = (GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling);
|
||||||
|
|
||||||
this->context->isSysDockInstalled = this->sysDockIntegration->getCurrentSysDockState();
|
this->context->isSysDockInstalled = this->sysDockIntegration->getCurrentSysDockState();
|
||||||
|
this->context->isSaltyNXInstalled = this->saltyNXIntegration->getCurrentSaltyNXState();
|
||||||
|
if(this->context->isSaltyNXInstalled) {
|
||||||
|
this->saltyNXIntegration->LoadSaltyNX();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
threadStart(&cpuGovernorTHREAD);
|
||||||
|
threadStart(&gpuGovernorTHREAD);
|
||||||
|
threadStart(&vrrTHREAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClockManager::~ClockManager()
|
ClockManager::~ClockManager()
|
||||||
{
|
{
|
||||||
threadClose(&cpuGovernorTHREAD);
|
threadClose(&cpuGovernorTHREAD);
|
||||||
threadClose(&gpuGovernorTHREAD);
|
threadClose(&gpuGovernorTHREAD);
|
||||||
|
threadClose(&vrrTHREAD);
|
||||||
|
|
||||||
|
delete this->sysDockIntegration;
|
||||||
|
delete this->saltyNXIntegration;
|
||||||
delete this->config;
|
delete this->config;
|
||||||
delete this->context;
|
delete this->context;
|
||||||
}
|
}
|
||||||
@@ -308,319 +339,233 @@ void ClockManager::RefreshFreqTableRow(SysClkModule module)
|
|||||||
FileUtils::LogLine("[mgr] count = %u", this->freqTable[module].count);
|
FileUtils::LogLine("[mgr] count = %u", this->freqTable[module].count);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 findIndex(u32 arr[], u32 size, u32 value) {
|
u32 ClockManager::SchedutilTargetHz(u32 util, u32 tableMaxHz) {
|
||||||
for (u32 i = 0; i < size; i++) {
|
u64 hz = (u64)tableMaxHz * util / STEP_UTIL;
|
||||||
if (arr[i] == value) {
|
return (u32)(std::min(hz, static_cast<u64>(tableMaxHz)));
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 findIndexMHz(u32 arr[], u32 size, u32 value) {
|
u32 ClockManager::TableIndexForHz(const FreqTable& table, u32 targetHz) { // must pass in a freqTable as tables are different for cpu/gpu
|
||||||
for (u32 i = 0; i < size; i++) {
|
for (u32 i = 0; i < table.count; i++)
|
||||||
if (arr[i] / 1000000 == value) {
|
if (table.list[i] >= targetHz)
|
||||||
return i;
|
return i;
|
||||||
}
|
return table.count - 1;
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClockManager::CpuGovernorThread(void* arg)
|
u32 ClockManager::ResolveTargetHz(ClockManager* mgr, SysClkModule module) {
|
||||||
{
|
u32 hz = mgr->context->overrideFreqs[module];
|
||||||
|
if (!hz)
|
||||||
|
hz = mgr->config->GetAutoClockHz(
|
||||||
|
mgr->context->applicationId, module,
|
||||||
|
mgr->context->profile, false);
|
||||||
|
if (!hz)
|
||||||
|
hz = mgr->config->GetAutoClockHz(
|
||||||
|
GLOBAL_PROFILE_ID, module,
|
||||||
|
mgr->context->profile, false);
|
||||||
|
return hz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClockManager::CpuGovernorThread(void* arg) {
|
||||||
ClockManager* mgr = static_cast<ClockManager*>(arg);
|
ClockManager* mgr = static_cast<ClockManager*>(arg);
|
||||||
|
|
||||||
for (;;)
|
u32 downHoldRemaining = 0;
|
||||||
{
|
u32 lastHz = 0;
|
||||||
if (!mgr->running)
|
|
||||||
{
|
for (;;) {
|
||||||
svcSleepThread(50'000'000);
|
if (!mgr->running || !isCpuGovernorEnabled) {
|
||||||
|
downHoldRemaining = 0;
|
||||||
|
lastHz = 0;
|
||||||
|
svcSleepThread(POLL_NS);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isCpuGovernorEnabled)
|
u32 mode = 0;
|
||||||
{
|
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
|
||||||
svcSleepThread(50'000'000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t mode = 0;
|
if (R_SUCCEEDED(rc) && apmExtIsBoostMode(mode)) {
|
||||||
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
|
|
||||||
bool isInBoostMode = R_SUCCEEDED(rc) && apmExtIsBoostMode(mode);
|
|
||||||
|
|
||||||
if (isInBoostMode)
|
|
||||||
{
|
|
||||||
isCpuGovernorInBoostMode = true;
|
isCpuGovernorInBoostMode = true;
|
||||||
svcSleepThread(50'000'000);
|
downHoldRemaining = 0;
|
||||||
continue;
|
lastHz = 0;
|
||||||
|
continue; // TODO: figure out a way to get boost clock easily and set it instead of just skipping the governor
|
||||||
|
} else if(!apmExtIsBoostMode(mode)) {
|
||||||
|
isCpuGovernorInBoostMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isCpuGovernorInBoostMode = false;
|
|
||||||
|
|
||||||
auto& table = mgr->freqTable[SysClkModule_CPU];
|
auto& table = mgr->freqTable[SysClkModule_CPU];
|
||||||
|
|
||||||
if (table.count == 0)
|
if (table.count == 0)
|
||||||
{
|
|
||||||
svcSleepThread(50'000'000);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
std::scoped_lock lock{mgr->contextMutex};
|
std::scoped_lock lock{mgr->contextMutex};
|
||||||
|
|
||||||
u32 currentHz = Board::GetHz(SysClkModule_CPU);
|
u32 cpuLoad = Board::GetPartLoad(HocClkPartLoad_CPUMax);
|
||||||
|
|
||||||
u32 index = table.count - 1;
|
|
||||||
for (u32 i = 0; i < table.count; i++)
|
|
||||||
{
|
|
||||||
if (table.list[i] == currentHz)
|
|
||||||
{
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table.list[index] != currentHz)
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < table.count; i++)
|
|
||||||
{
|
|
||||||
if (table.list[i] >= currentHz)
|
|
||||||
{
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 targetHz = mgr->context->overrideFreqs[SysClkModule_CPU];
|
|
||||||
if (!targetHz)
|
|
||||||
{
|
|
||||||
targetHz = mgr->config->GetAutoClockHz(
|
|
||||||
mgr->context->applicationId,
|
|
||||||
SysClkModule_CPU,
|
|
||||||
mgr->context->profile,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!targetHz)
|
|
||||||
{
|
|
||||||
targetHz = mgr->config->GetAutoClockHz(
|
|
||||||
GLOBAL_PROFILE_ID,
|
|
||||||
SysClkModule_CPU,
|
|
||||||
mgr->context->profile,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int gpuLoad = Board::GetPartLoad(HocClkPartLoad_GPU);
|
|
||||||
int cpuLoad = Board::GetPartLoad(HocClkPartLoad_CPUMax);
|
|
||||||
|
|
||||||
if (isGpuGovernorEnabled && gpuLoad < 800)
|
|
||||||
{
|
|
||||||
if (cpuLoad < 600 && index > 0)
|
|
||||||
{
|
|
||||||
index--;
|
|
||||||
}
|
|
||||||
else if (cpuLoad > 800 && index + 1 < table.count)
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (cpuLoad < 600 && index > 0)
|
|
||||||
{
|
|
||||||
index--;
|
|
||||||
}
|
|
||||||
else if (cpuLoad > 800 && index + 1 < table.count)
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
u32 tableMaxHz = table.list[table.count - 1];
|
||||||
|
u32 desiredHz = ClockManager::SchedutilTargetHz(cpuLoad, tableMaxHz);
|
||||||
|
u32 targetHz = ClockManager::ResolveTargetHz(mgr, SysClkModule_CPU);
|
||||||
u32 maxHz = mgr->GetMaxAllowedHz(SysClkModule_CPU, mgr->context->profile);
|
u32 maxHz = mgr->GetMaxAllowedHz(SysClkModule_CPU, mgr->context->profile);
|
||||||
|
|
||||||
if (targetHz)
|
if (targetHz && desiredHz > targetHz)
|
||||||
{
|
desiredHz = targetHz;
|
||||||
u32 targetIndex = table.count - 1;
|
|
||||||
for (u32 i = 0; i < table.count; i++)
|
|
||||||
{
|
|
||||||
if (table.list[i] >= targetHz)
|
|
||||||
{
|
|
||||||
targetIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > targetIndex)
|
if (maxHz && desiredHz > maxHz)
|
||||||
{
|
desiredHz = maxHz;
|
||||||
index = targetIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxHz > 0 && table.list[index] > maxHz)
|
u32 newHz = table.list[ClockManager::TableIndexForHz(table, desiredHz)];
|
||||||
{
|
|
||||||
for (u32 i = table.count; i > 0; i--)
|
|
||||||
{
|
|
||||||
if (table.list[i - 1] <= maxHz)
|
|
||||||
{
|
|
||||||
index = i - 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 newHz = table.list[index];
|
// ramp up fast, go down slow
|
||||||
if (mgr->IsAssignableHz(SysClkModule_CPU, newHz))
|
bool goingDown = (lastHz != 0) && (newHz < lastHz);
|
||||||
{
|
|
||||||
|
if (!goingDown)
|
||||||
|
downHoldRemaining = 0;
|
||||||
|
else if (downHoldRemaining == 0)
|
||||||
|
downHoldRemaining = DOWN_HOLD_TICKS;
|
||||||
|
|
||||||
|
if (downHoldRemaining > 0)
|
||||||
|
downHoldRemaining--;
|
||||||
|
|
||||||
|
if ((!goingDown || (downHoldRemaining == 0)) && mgr->IsAssignableHz(SysClkModule_CPU, newHz)) {
|
||||||
Board::SetHz(SysClkModule_CPU, newHz);
|
Board::SetHz(SysClkModule_CPU, newHz);
|
||||||
mgr->context->freqs[SysClkModule_CPU] = newHz;
|
mgr->context->freqs[SysClkModule_CPU] = newHz;
|
||||||
|
lastHz = newHz;
|
||||||
}
|
}
|
||||||
|
|
||||||
svcSleepThread(50'000'000);
|
svcSleepThread(POLL_NS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClockManager::GovernorThread(void* arg)
|
void ClockManager::GovernorThread(void* arg) {
|
||||||
{
|
|
||||||
ClockManager* mgr = static_cast<ClockManager*>(arg);
|
ClockManager* mgr = static_cast<ClockManager*>(arg);
|
||||||
|
|
||||||
for (;;)
|
u32 downHoldRemaining = 0;
|
||||||
{
|
u32 lastHz = 0;
|
||||||
if (!mgr->running)
|
|
||||||
{
|
|
||||||
svcSleepThread(50'000'000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isGpuGovernorEnabled)
|
for (;;) {
|
||||||
{
|
if (!mgr->running || !isGpuGovernorEnabled) {
|
||||||
svcSleepThread(50'000'000);
|
downHoldRemaining = 0;
|
||||||
|
lastHz = 0;
|
||||||
|
svcSleepThread(POLL_NS);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& table = mgr->freqTable[SysClkModule_GPU];
|
auto& table = mgr->freqTable[SysClkModule_GPU];
|
||||||
if (table.count == 0)
|
if (table.count == 0)
|
||||||
{
|
continue;
|
||||||
svcSleepThread(50'000'000);
|
|
||||||
|
std::scoped_lock lock{mgr->contextMutex};
|
||||||
|
|
||||||
|
u32 gpuLoad = Board::GetPartLoad(HocClkPartLoad_GPU);
|
||||||
|
u32 tableMaxHz = table.list[table.count - 1];
|
||||||
|
u32 desiredHz = ClockManager::SchedutilTargetHz(gpuLoad, tableMaxHz);
|
||||||
|
u32 targetHz = ClockManager::ResolveTargetHz(mgr, SysClkModule_GPU);
|
||||||
|
u32 maxHz = mgr->GetMaxAllowedHz(SysClkModule_GPU, mgr->context->profile);
|
||||||
|
|
||||||
|
if (targetHz && desiredHz > targetHz)
|
||||||
|
desiredHz = targetHz;
|
||||||
|
|
||||||
|
if (maxHz && desiredHz > maxHz)
|
||||||
|
desiredHz = maxHz;
|
||||||
|
|
||||||
|
u32 newHz = table.list[ClockManager::TableIndexForHz(table, desiredHz)];
|
||||||
|
bool goingDown = (lastHz != 0) && (newHz < lastHz);
|
||||||
|
|
||||||
|
if (!goingDown)
|
||||||
|
downHoldRemaining = 0;
|
||||||
|
else if (downHoldRemaining == 0)
|
||||||
|
downHoldRemaining = DOWN_HOLD_TICKS;
|
||||||
|
|
||||||
|
if (downHoldRemaining > 0)
|
||||||
|
downHoldRemaining--;
|
||||||
|
|
||||||
|
if ((!goingDown || (downHoldRemaining == 0)) && mgr->IsAssignableHz(SysClkModule_GPU, newHz)) {
|
||||||
|
Board::SetHz(SysClkModule_GPU, newHz);
|
||||||
|
mgr->context->freqs[SysClkModule_GPU] = newHz;
|
||||||
|
lastHz = newHz;
|
||||||
|
}
|
||||||
|
|
||||||
|
svcSleepThread(POLL_NS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClockManager::VRRThread(void* arg) {
|
||||||
|
ClockManager* mgr = static_cast<ClockManager*>(arg);
|
||||||
|
u8 tick = 0;
|
||||||
|
for (;;) {
|
||||||
|
if (!mgr->running || mgr->context->profile == SysClkProfile_Docked || !isVRREnabled) {
|
||||||
|
svcSleepThread(POLL_NS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Board::IsHoag()) { // don't do anything on lite
|
||||||
|
svcSleepThread(~0ULL);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock lock{mgr->contextMutex};
|
std::scoped_lock lock{mgr->contextMutex};
|
||||||
|
|
||||||
u32 currentHz = Board::GetHz(SysClkModule_GPU);
|
u8 fps;
|
||||||
|
|
||||||
u32 index = table.count - 1;
|
if(mgr->context->isSaltyNXInstalled) {
|
||||||
for (u32 i = 0; i < table.count; i++)
|
fps = mgr->saltyNXIntegration->GetFPS();
|
||||||
{
|
} else {
|
||||||
if (table.list[i] == currentHz)
|
svcSleepThread(~0ULL); // effectively disable the thread if SaltyNX isn't installed, as there's no point in it running
|
||||||
{
|
continue;
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (table.list[index] != currentHz)
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < table.count; i++)
|
|
||||||
{
|
|
||||||
if (table.list[i] >= currentHz)
|
|
||||||
{
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 targetHz = mgr->context->overrideFreqs[SysClkModule_GPU];
|
if(fps == 254) {
|
||||||
|
svcSleepThread(POLL_NS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// if(appletGetFocusState() != AppletFocusState_InFocus) {
|
||||||
|
// Board::ResetToStockDisplay();
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
u32 targetHz = mgr->context->overrideFreqs[HorizonOCModule_Display];
|
||||||
if (!targetHz)
|
if (!targetHz)
|
||||||
{
|
{
|
||||||
targetHz = mgr->config->GetAutoClockHz(
|
targetHz = mgr->config->GetAutoClockHz(mgr->context->applicationId, HorizonOCModule_Display, mgr->context->profile, false);
|
||||||
mgr->context->applicationId,
|
if(!targetHz)
|
||||||
SysClkModule_GPU,
|
targetHz = mgr->config->GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Display, mgr->context->profile, false);
|
||||||
mgr->context->profile,
|
}
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!targetHz)
|
u8 maxDisplay;
|
||||||
{
|
if(targetHz) {
|
||||||
targetHz = mgr->config->GetAutoClockHz(
|
maxDisplay = targetHz;
|
||||||
GLOBAL_PROFILE_ID,
|
} else {
|
||||||
SysClkModule_GPU,
|
if(Board::GetConsoleType() == HorizonOCConsoleType_Aula) {
|
||||||
mgr->context->profile,
|
maxDisplay = mgr->config->GetConfigValue(HorizonOCConfigValue_EnableUnsafeDisplayFreqs) ? 65 : 60;
|
||||||
false
|
} else {
|
||||||
);
|
maxDisplay = mgr->config->GetConfigValue(HorizonOCConfigValue_EnableUnsafeDisplayFreqs) ? 72 : 60;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int gpuLoad = Board::GetPartLoad(HocClkPartLoad_GPU);
|
u8 minDisplay = Board::GetConsoleType() == HorizonOCConsoleType_Aula ? 45 : 40;
|
||||||
int cpuLoad = Board::GetPartLoad(HocClkPartLoad_CPUMax);
|
if(maxDisplay == minDisplay)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (isCpuGovernorEnabled && !isCpuGovernorInBoostMode && cpuLoad < 600)
|
if(fps >= minDisplay && fps <= maxDisplay)
|
||||||
{
|
Board::SetHz(HorizonOCModule_Display, fps);
|
||||||
if (gpuLoad < 600 && index > 0)
|
else {
|
||||||
{
|
for(u32 i = 0; i < 10; i++) {
|
||||||
index--;
|
u32 compareHz = fps * i;
|
||||||
}
|
if(compareHz >= minDisplay && compareHz <= maxDisplay) {
|
||||||
else if (gpuLoad > 750 && index + 1 < table.count)
|
Board::SetHz(HorizonOCModule_Display, compareHz);
|
||||||
{
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (gpuLoad < 600 && index > 0)
|
|
||||||
{
|
|
||||||
index--;
|
|
||||||
}
|
|
||||||
else if (gpuLoad > 800 && index + 1 < table.count)
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 maxHz = mgr->GetMaxAllowedHz(SysClkModule_GPU, mgr->context->profile);
|
|
||||||
|
|
||||||
if (targetHz)
|
|
||||||
{
|
|
||||||
u32 targetIndex = table.count - 1;
|
|
||||||
for (u32 i = 0; i < table.count; i++)
|
|
||||||
{
|
|
||||||
if (table.list[i] >= targetHz)
|
|
||||||
{
|
|
||||||
targetIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > targetIndex)
|
|
||||||
{
|
|
||||||
index = targetIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxHz > 0 && table.list[index] > maxHz)
|
|
||||||
{
|
|
||||||
for (u32 i = table.count; i > 0; i--)
|
|
||||||
{
|
|
||||||
if (table.list[i - 1] <= maxHz)
|
|
||||||
{
|
|
||||||
index = i - 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(++tick > 50) {
|
||||||
u32 newHz = table.list[index];
|
Board::ResetToStockDisplay();
|
||||||
if (mgr->IsAssignableHz(SysClkModule_GPU, newHz))
|
tick = 0;
|
||||||
{
|
svcSleepThread(25'000'000);
|
||||||
Board::SetHz(SysClkModule_GPU, newHz);
|
|
||||||
mgr->context->freqs[SysClkModule_GPU] = newHz;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svcSleepThread(50'000'000);
|
svcSleepThread(POLL_NS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GovernorState ClockManager::GetEffectiveGovernorState(GovernorState appState, GovernorState tempState)
|
GovernorState ClockManager::GetEffectiveGovernorState(GovernorState appState, GovernorState tempState)
|
||||||
{
|
{
|
||||||
if (tempState == GovernorState_Disabled)
|
if (tempState == GovernorState_Disabled)
|
||||||
@@ -676,25 +621,41 @@ void ClockManager::HandleGovernor(uint32_t targetHz) {
|
|||||||
|
|
||||||
GovernorState effectiveState = this->GetEffectiveGovernorState(appGovernorState, tempGovernorState);
|
GovernorState effectiveState = this->GetEffectiveGovernorState(appGovernorState, tempGovernorState);
|
||||||
|
|
||||||
bool newCpuGovernorState = (effectiveState == GovernorState_Enabled_CpuGpu || effectiveState == GovernorState_Enabled_Cpu);
|
bool newCpuGovernorState = (effectiveState == GovernorState_Enabled_CpuGpuVrr ||
|
||||||
bool newGpuGovernorState = (effectiveState == GovernorState_Enabled_CpuGpu || effectiveState == GovernorState_Enabled_Gpu);
|
effectiveState == GovernorState_Enabled_CpuVrr ||
|
||||||
|
effectiveState == GovernorState_Enabled_CpuGpu ||
|
||||||
|
effectiveState == GovernorState_Enabled_Cpu);
|
||||||
|
|
||||||
|
bool newGpuGovernorState = (effectiveState == GovernorState_Enabled_CpuGpuVrr ||
|
||||||
|
effectiveState == GovernorState_Enabled_GpuVrr ||
|
||||||
|
effectiveState == GovernorState_Enabled_CpuGpu ||
|
||||||
|
effectiveState == GovernorState_Enabled_Gpu);
|
||||||
|
|
||||||
|
bool newVrrGovernorState = (effectiveState == GovernorState_Enabled_CpuGpuVrr ||
|
||||||
|
effectiveState == GovernorState_Enabled_CpuVrr ||
|
||||||
|
effectiveState == GovernorState_Enabled_GpuVrr ||
|
||||||
|
effectiveState == GovernorState_Enabled_Vrr);
|
||||||
|
|
||||||
isCpuGovernorEnabled = newCpuGovernorState;
|
isCpuGovernorEnabled = newCpuGovernorState;
|
||||||
isGpuGovernorEnabled = newGpuGovernorState;
|
isGpuGovernorEnabled = newGpuGovernorState;
|
||||||
|
isVRREnabled = newVrrGovernorState;
|
||||||
if(newCpuGovernorState == false && lastCpuGovernorState == true) {
|
if(newCpuGovernorState == false && lastCpuGovernorState == true) {
|
||||||
svcSleepThread(150'000'000); // thread syncing. probably a cleaner way to do this but hey, it works!
|
svcSleepThread(50'000'000); // thread syncing. probably a cleaner way to do this but hey, it works!
|
||||||
Board::ResetToStockCpu();
|
Board::ResetToStockCpu();
|
||||||
}
|
}
|
||||||
if(newGpuGovernorState == false && lastGpuGovernorState == true) {
|
if(newGpuGovernorState == false && lastGpuGovernorState == true) {
|
||||||
svcSleepThread(150'000'000);
|
svcSleepThread(50'000'000);
|
||||||
Board::ResetToStockGpu();
|
Board::ResetToStockGpu();
|
||||||
}
|
}
|
||||||
|
if (newVrrGovernorState == false && lastVrrGovernorState == true) {
|
||||||
if(newCpuGovernorState != lastCpuGovernorState || newGpuGovernorState != lastGpuGovernorState) {
|
svcSleepThread(50'000'000);
|
||||||
FileUtils::LogLine("[mgr] Governor state changed: CPU %s, GPU %s", newCpuGovernorState ? "enabled" : "disabled", newGpuGovernorState ? "enabled" : "disabled");
|
Board::ResetToStockDisplay();
|
||||||
|
}
|
||||||
|
if(newCpuGovernorState != lastCpuGovernorState || newGpuGovernorState != lastGpuGovernorState || newVrrGovernorState != lastVrrGovernorState) {
|
||||||
|
FileUtils::LogLine("[mgr] Governor state changed: CPU %s, GPU %s, VRR %s", newCpuGovernorState ? "enabled" : "disabled", newGpuGovernorState ? "enabled" : "disabled", newVrrGovernorState ? "enabled" : "disabled");
|
||||||
lastCpuGovernorState = newCpuGovernorState;
|
lastCpuGovernorState = newCpuGovernorState;
|
||||||
lastGpuGovernorState = newGpuGovernorState;
|
lastGpuGovernorState = newGpuGovernorState;
|
||||||
|
lastVrrGovernorState = newVrrGovernorState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,24 +675,23 @@ void ClockManager::DVFSBeforeSet(u32 targetHz) {
|
|||||||
|
|
||||||
void ClockManager::DVFSAfterSet(u32 targetHz) {
|
void ClockManager::DVFSAfterSet(u32 targetHz) {
|
||||||
s32 dvfsOffset = this->config->GetConfigValue(HorizonOCConfigValue_DVFSOffset);
|
s32 dvfsOffset = this->config->GetConfigValue(HorizonOCConfigValue_DVFSOffset);
|
||||||
dvfsOffset = std::max(dvfsOffset, -50);
|
dvfsOffset = std::max(dvfsOffset, -80);
|
||||||
u32 vmin = Board::GetMinimumGpuVoltage(targetHz / 1000000) + dvfsOffset;
|
u32 vmin = Board::GetMinimumGpuVoltage(targetHz / 1000000);
|
||||||
Board::PcvHijackDvfs(vmin);
|
|
||||||
|
|
||||||
targetHz = this->context->overrideFreqs[SysClkModule_GPU];
|
if (vmin) {
|
||||||
if (!targetHz)
|
vmin += dvfsOffset;
|
||||||
{
|
|
||||||
targetHz = this->config->GetAutoClockHz(this->context->applicationId, SysClkModule_GPU, this->context->profile, false);
|
|
||||||
if(!targetHz)
|
|
||||||
targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, SysClkModule_GPU, this->context->profile, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 maxHz = this->GetMaxAllowedHz(SysClkModule_GPU, this->context->profile);
|
u32 maxHz = this->GetMaxAllowedHz(SysClkModule_GPU, this->context->profile);
|
||||||
u32 nearestHz = this->GetNearestHz(SysClkModule_GPU, targetHz, maxHz);
|
u32 nearestHz = this->GetNearestHz(SysClkModule_GPU, targetHz, maxHz);
|
||||||
|
Board::PcvHijackDvfs(vmin);
|
||||||
|
|
||||||
if(targetHz) {
|
if (targetHz) {
|
||||||
Board::SetHz(SysClkModule_GPU, ~0);
|
Board::SetHz(SysClkModule_GPU, ~0);
|
||||||
Board::SetHz(SysClkModule_GPU, nearestHz);
|
Board::SetHz(SysClkModule_GPU, nearestHz);
|
||||||
|
} else {
|
||||||
|
Board::SetHz(SysClkModule_GPU, ~0);
|
||||||
|
Board::ResetToStockGpu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,7 +731,7 @@ void ClockManager::HandleFreqReset(SysClkModule module, bool isBoost) {
|
|||||||
case SysClkModule_CPU:
|
case SysClkModule_CPU:
|
||||||
if(!(isBoost || (this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode) && isBoost)))
|
if(!(isBoost || (this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode) && isBoost)))
|
||||||
Board::ResetToStockCpu();
|
Board::ResetToStockCpu();
|
||||||
if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv) || (kipAvailable && Board::GetSocType() == SysClkSocType_Erista)) {
|
if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv)) {
|
||||||
if(Board::GetSocType() == SysClkSocType_Erista)
|
if(Board::GetSocType() == SysClkSocType_Erista)
|
||||||
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
|
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
|
||||||
else
|
else
|
||||||
@@ -785,6 +745,12 @@ void ClockManager::HandleFreqReset(SysClkModule module, bool isBoost) {
|
|||||||
case SysClkModule_MEM:
|
case SysClkModule_MEM:
|
||||||
Board::ResetToStockMem();
|
Board::ResetToStockMem();
|
||||||
DVFSReset();
|
DVFSReset();
|
||||||
|
break;
|
||||||
|
case HorizonOCModule_Display:
|
||||||
|
if(this->config->GetConfigValue(HorizonOCConfigValue_OverwriteRefreshRate) && !Board::IsHoag()) {
|
||||||
|
Board::ResetToStockDisplay();
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -807,7 +773,7 @@ void ClockManager::SetClocks(bool isBoost) {
|
|||||||
bool returnRaw = false; // Return a value scaled to MHz instead of raw value
|
bool returnRaw = false; // Return a value scaled to MHz instead of raw value
|
||||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||||
{
|
{
|
||||||
u32 oldHz = Board::GetHz((SysClkModule)module); // Get Old RAM hz (used primarily for DVFS Logic)
|
u32 oldHz = Board::GetHz((SysClkModule)module); // Get Old hz (used primarily for DVFS Logic)
|
||||||
|
|
||||||
if(module > SysClkModule_MEM)
|
if(module > SysClkModule_MEM)
|
||||||
returnRaw = true;
|
returnRaw = true;
|
||||||
@@ -825,11 +791,19 @@ void ClockManager::SetClocks(bool isBoost) {
|
|||||||
HandleGovernor(targetHz);
|
HandleGovernor(targetHz);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(module == HorizonOCModule_Display && this->config->GetConfigValue(HorizonOCConfigValue_OverwriteRefreshRate) && Board::GetConsoleType() != HorizonOCConsoleType_Hoag) {
|
bool noCPU = isCpuGovernorEnabled;
|
||||||
if(targetHz)
|
bool noGPU = isGpuGovernorEnabled;
|
||||||
Board::SetHz(HorizonOCModule_Display, targetHz);
|
if(!Board::IsHoag()) {
|
||||||
else
|
bool noDisp = isVRREnabled;
|
||||||
Board::ResetToStockDisplay();
|
if(noDisp && module == HorizonOCModule_Display)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(module == HorizonOCModule_Display && this->config->GetConfigValue(HorizonOCConfigValue_OverwriteRefreshRate)) {
|
||||||
|
if(targetHz)
|
||||||
|
Board::SetHz(HorizonOCModule_Display, targetHz);
|
||||||
|
else
|
||||||
|
Board::ResetToStockDisplay();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip GPU and CPU if governors handle them
|
// Skip GPU and CPU if governors handle them
|
||||||
@@ -837,8 +811,6 @@ void ClockManager::SetClocks(bool isBoost) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool noCPU = isCpuGovernorEnabled;
|
|
||||||
bool noGPU = isGpuGovernorEnabled;
|
|
||||||
|
|
||||||
if(noCPU && module == SysClkModule_CPU)
|
if(noCPU && module == SysClkModule_CPU)
|
||||||
continue;
|
continue;
|
||||||
@@ -865,8 +837,7 @@ void ClockManager::SetClocks(bool isBoost) {
|
|||||||
Board::SetHz((SysClkModule)module, nearestHz);
|
Board::SetHz((SysClkModule)module, nearestHz);
|
||||||
this->context->freqs[module] = nearestHz;
|
this->context->freqs[module] = nearestHz;
|
||||||
|
|
||||||
if(module == SysClkModule_CPU && (this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv) || (kipAvailable && Board::GetSocType() == SysClkSocType_Erista)))
|
if(module == SysClkModule_CPU && (this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv))) {
|
||||||
{
|
|
||||||
HandleCpuUv();
|
HandleCpuUv();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -901,7 +872,7 @@ void ClockManager::Tick()
|
|||||||
|
|
||||||
void ClockManager::ResetToStockClocks() {
|
void ClockManager::ResetToStockClocks() {
|
||||||
Board::ResetToStockCpu();
|
Board::ResetToStockCpu();
|
||||||
if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv) || (kipAvailable && Board::GetSocType() == SysClkSocType_Erista))
|
if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv))
|
||||||
{
|
{
|
||||||
if(Board::GetSocType() == SysClkSocType_Erista)
|
if(Board::GetSocType() == SysClkSocType_Erista)
|
||||||
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
|
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
|
||||||
@@ -1037,20 +1008,24 @@ bool ClockManager::RefreshContext()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this->context->maxDisplayFreq = Board::GetHighestDockedDisplayRate();
|
// this->context->maxDisplayFreq = Board::GetHighestDockedDisplayRate();
|
||||||
|
if(!Board::IsHoag()) {
|
||||||
|
u32 targetHz = this->context->overrideFreqs[HorizonOCModule_Display];
|
||||||
|
if (!targetHz)
|
||||||
|
{
|
||||||
|
targetHz = this->config->GetAutoClockHz(this->context->applicationId, HorizonOCModule_Display, this->context->profile, true);
|
||||||
|
if(!targetHz)
|
||||||
|
targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Display, this->context->profile, true);
|
||||||
|
}
|
||||||
|
|
||||||
u32 targetHz = this->context->overrideFreqs[HorizonOCModule_Display];
|
if(targetHz && this->context->realFreqs[HorizonOCModule_Display] > targetHz && this->context->profile != SysClkProfile_Docked)
|
||||||
if (!targetHz)
|
this->context->realFreqs[HorizonOCModule_Display] = targetHz; // clean up display real freqs, should probably be moved to the real freqs loop?
|
||||||
{
|
|
||||||
targetHz = this->config->GetAutoClockHz(this->context->applicationId, HorizonOCModule_Display, this->context->profile, true);
|
|
||||||
if(!targetHz)
|
|
||||||
targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Display, this->context->profile, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(targetHz && this->context->realFreqs[HorizonOCModule_Display] > targetHz && this->context->profile != SysClkProfile_Docked)
|
|
||||||
this->context->realFreqs[HorizonOCModule_Display] = targetHz; // clean up display real freqs, should probably be moved to the real freqs loop?
|
|
||||||
|
|
||||||
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag)
|
|
||||||
Board::SetDisplayRefreshDockedState(this->context->profile == SysClkProfile_Docked);
|
Board::SetDisplayRefreshDockedState(this->context->profile == SysClkProfile_Docked);
|
||||||
|
}
|
||||||
|
if(this->context->isSaltyNXInstalled)
|
||||||
|
this->context->fps = saltyNXIntegration->GetFPS();
|
||||||
|
else
|
||||||
|
this->context->fps = 254; // N/A
|
||||||
|
|
||||||
return hasChanged;
|
return hasChanged;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "integrations.h"
|
#include "integrations.h"
|
||||||
|
|
||||||
class SysDockIntegration;
|
class SysDockIntegration;
|
||||||
|
class SaltyNXIntegration;
|
||||||
class ClockManager
|
class ClockManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -184,6 +185,13 @@ class ClockManager
|
|||||||
*/
|
*/
|
||||||
static void GovernorThread(void* arg);
|
static void GovernorThread(void* arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the VRR Algorithm
|
||||||
|
*
|
||||||
|
* @param arg Cast to ClockManager* for context
|
||||||
|
*/
|
||||||
|
static void VRRThread(void* arg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the effective governor state from application/temporary override
|
* Gets the effective governor state from application/temporary override
|
||||||
*
|
*
|
||||||
@@ -193,10 +201,10 @@ class ClockManager
|
|||||||
GovernorState GetEffectiveGovernorState(GovernorState appState, GovernorState tempState);
|
GovernorState GetEffectiveGovernorState(GovernorState appState, GovernorState tempState);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frequency tables
|
* Frequency table
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct {
|
struct FreqTable {
|
||||||
std::uint32_t count;
|
std::uint32_t count;
|
||||||
std::uint32_t list[SYSCLK_FREQ_LIST_MAX];
|
std::uint32_t list[SYSCLK_FREQ_LIST_MAX];
|
||||||
} freqTable[SysClkModule_EnumMax];
|
} freqTable[SysClkModule_EnumMax];
|
||||||
@@ -216,6 +224,30 @@ class ClockManager
|
|||||||
*/
|
*/
|
||||||
unsigned int GetGpuVoltage (unsigned int freq, int speedo);
|
unsigned int GetGpuVoltage (unsigned int freq, int speedo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the required vMin for a ram frequency for a speedo
|
||||||
|
*
|
||||||
|
* @param util Utilization in percentile
|
||||||
|
* @param tableMaxHz Table Max Hz
|
||||||
|
*/
|
||||||
|
static u32 SchedutilTargetHz(u32 util, u32 tableMaxHz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the required vMin for a ram frequency for a speedo
|
||||||
|
*
|
||||||
|
* @param table FreqTable for module
|
||||||
|
* @param targetHz Hz to search for
|
||||||
|
*/
|
||||||
|
static u32 TableIndexForHz(const FreqTable& table, u32 targetHz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the required vMin for a ram frequency for a speedo
|
||||||
|
*
|
||||||
|
* @param mgr ClockManager instance (runs in a thread so must be passed)
|
||||||
|
* @param module Module for which to resolve target Hz
|
||||||
|
*/
|
||||||
|
static u32 ResolveTargetHz(ClockManager* mgr, SysClkModule module);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool IsAssignableHz(SysClkModule module, std::uint32_t hz);
|
bool IsAssignableHz(SysClkModule module, std::uint32_t hz);
|
||||||
inline std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile);
|
inline std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile);
|
||||||
@@ -233,4 +265,5 @@ class ClockManager
|
|||||||
std::uint64_t lastPowerLogNs;
|
std::uint64_t lastPowerLogNs;
|
||||||
std::uint64_t lastCsvWriteNs;
|
std::uint64_t lastCsvWriteNs;
|
||||||
SysDockIntegration *sysDockIntegration;
|
SysDockIntegration *sysDockIntegration;
|
||||||
|
SaltyNXIntegration *saltyNXIntegration;
|
||||||
};
|
};
|
||||||
@@ -18,15 +18,93 @@
|
|||||||
|
|
||||||
#include "integrations.h"
|
#include "integrations.h"
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <SaltyNX.h>
|
||||||
|
#include "process_management.h"
|
||||||
|
|
||||||
SysDockIntegration::SysDockIntegration() {
|
SysDockIntegration::SysDockIntegration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SysDockIntegration::getCurrentSysDockState() {
|
bool SysDockIntegration::getCurrentSysDockState() {
|
||||||
struct stat st = {0};
|
struct stat st = {0};
|
||||||
if (stat("sdmc:/atmosphere/contents/42000000000000A0", &st) == 0 && S_ISDIR(st.st_mode)) {
|
return stat("sdmc:/atmosphere/contents/42000000000000A0/flags/boot2.flag", &st) == 0;
|
||||||
return true;
|
}
|
||||||
} else {
|
|
||||||
return false;
|
SaltyNXIntegration::SaltyNXIntegration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SaltyNXIntegration::LoadSaltyNX() {
|
||||||
|
if (!CheckPort())
|
||||||
|
return;
|
||||||
|
LoadSharedMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SaltyNXIntegration::getCurrentSaltyNXState() {
|
||||||
|
struct stat st = {0};
|
||||||
|
return stat("sdmc:/atmosphere/contents/0000000000534C56/flags/boot2.flag", &st) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SaltyNXIntegration::CheckPort() {
|
||||||
|
Handle saltysd;
|
||||||
|
|
||||||
|
for (int i = 0; i < 67; i++) {
|
||||||
|
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||||
|
svcCloseHandle(saltysd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == 66) return false;
|
||||||
|
svcSleepThread(1'000'000);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 67; i++) {
|
||||||
|
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||||
|
svcCloseHandle(saltysd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
svcSleepThread(1'000'000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaltyNXIntegration::LoadSharedMemory() {
|
||||||
|
if (SaltySD_Connect())
|
||||||
|
return;
|
||||||
|
SaltySD_GetSharedMemoryHandle(&remoteSharedMemory);
|
||||||
|
SaltySD_Term();
|
||||||
|
shmemLoadRemote(&_sharedmemory, remoteSharedMemory, 0x1000, Perm_Rw);
|
||||||
|
if (!shmemMap(&_sharedmemory))
|
||||||
|
SharedMemoryUsed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaltyNXIntegration::searchSharedMemoryBlock(uintptr_t base) {
|
||||||
|
ptrdiff_t search_offset = 0;
|
||||||
|
while (search_offset < 0x1000) {
|
||||||
|
NxFps = (NxFpsSharedBlock*)(base + search_offset);
|
||||||
|
if (NxFps->MAGIC == 0x465053)
|
||||||
|
return;
|
||||||
|
search_offset += 4;
|
||||||
|
}
|
||||||
|
NxFps = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 prevTid = 0;
|
||||||
|
u8 SaltyNXIntegration::GetFPS() {
|
||||||
|
if (!SharedMemoryUsed)
|
||||||
|
return 254;
|
||||||
|
|
||||||
|
u64 tid = ProcessManagement::GetCurrentApplicationId();
|
||||||
|
if (tid == 0)
|
||||||
|
return 254;
|
||||||
|
|
||||||
|
if (prevTid != tid) {
|
||||||
|
NxFps = 0;
|
||||||
|
prevTid = tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NxFps) {
|
||||||
|
uintptr_t base = (uintptr_t)shmemGetAddr(&_sharedmemory);
|
||||||
|
searchSharedMemoryBlock(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NxFps ? NxFps->FPS : 254;
|
||||||
}
|
}
|
||||||
@@ -35,3 +35,59 @@ public:
|
|||||||
|
|
||||||
bool getCurrentSysDockState();
|
bool getCurrentSysDockState();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SaltyNXIntegration {
|
||||||
|
public:
|
||||||
|
struct resolutionCalls {
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
uint16_t calls;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NxFpsSharedBlock {
|
||||||
|
uint32_t MAGIC;
|
||||||
|
uint8_t FPS;
|
||||||
|
float FPSavg;
|
||||||
|
bool pluginActive;
|
||||||
|
uint8_t FPSlocked;
|
||||||
|
uint8_t FPSmode;
|
||||||
|
uint8_t ZeroSync;
|
||||||
|
uint8_t patchApplied;
|
||||||
|
uint8_t API;
|
||||||
|
uint32_t FPSticks[10];
|
||||||
|
uint8_t Buffers;
|
||||||
|
uint8_t SetBuffers;
|
||||||
|
uint8_t ActiveBuffers;
|
||||||
|
uint8_t SetActiveBuffers;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool handheld: 1;
|
||||||
|
bool docked: 1;
|
||||||
|
unsigned int reserved: 6;
|
||||||
|
} NX_PACKED ds;
|
||||||
|
uint8_t general;
|
||||||
|
} displaySync;
|
||||||
|
resolutionCalls renderCalls[8];
|
||||||
|
resolutionCalls viewportCalls[8];
|
||||||
|
bool forceOriginalRefreshRate;
|
||||||
|
bool dontForce60InDocked;
|
||||||
|
bool forceSuspend;
|
||||||
|
uint8_t currentRefreshRate;
|
||||||
|
float readSpeedPerSecond;
|
||||||
|
uint8_t FPSlockedDocked;
|
||||||
|
uint64_t frameNumber;
|
||||||
|
} NX_PACKED;
|
||||||
|
|
||||||
|
NxFpsSharedBlock* NxFps = 0;
|
||||||
|
SharedMemory _sharedmemory = {};
|
||||||
|
bool SharedMemoryUsed = false;
|
||||||
|
Handle remoteSharedMemory = 1;
|
||||||
|
SaltyNXIntegration();
|
||||||
|
void LoadSaltyNX();
|
||||||
|
bool getCurrentSaltyNXState();
|
||||||
|
|
||||||
|
bool CheckPort();
|
||||||
|
void LoadSharedMemory();
|
||||||
|
void searchSharedMemoryBlock(uintptr_t base);
|
||||||
|
u8 GetFPS();
|
||||||
|
};
|
||||||
@@ -119,9 +119,15 @@ Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8*
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SysClkIpcCmd_GetCurrentContext:
|
case SysClkIpcCmd_GetCurrentContext:
|
||||||
*out_dataSize = sizeof(SysClkContext);
|
if(r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1)
|
||||||
return ipcSrv->GetCurrentContext((SysClkContext*)out_data);
|
{
|
||||||
|
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||||
|
if(bufSize >= sizeof(SysClkContext))
|
||||||
|
{
|
||||||
|
return ipcSrv->GetCurrentContext((SysClkContext*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case SysClkIpcCmd_Exit:
|
case SysClkIpcCmd_Exit:
|
||||||
return ipcSrv->Exit();
|
return ipcSrv->Exit();
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,11 @@
|
|||||||
#include "ipc_service.h"
|
#include "ipc_service.h"
|
||||||
#define INNER_HEAP_SIZE 0x40000
|
#define INNER_HEAP_SIZE 0x40000
|
||||||
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
void virtmemSetup(void);
|
||||||
|
|
||||||
extern std::uint32_t __start__;
|
extern std::uint32_t __start__;
|
||||||
|
|
||||||
std::uint32_t __nx_applet_type = AppletType_None;
|
std::uint32_t __nx_applet_type = AppletType_None;
|
||||||
@@ -62,6 +65,8 @@ extern "C"
|
|||||||
|
|
||||||
fake_heap_start = (char*)addr;
|
fake_heap_start = (char*)addr;
|
||||||
fake_heap_end = (char*)addr + size;
|
fake_heap_end = (char*)addr + size;
|
||||||
|
|
||||||
|
virtmemSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void __appInit(void)
|
void __appInit(void)
|
||||||
@@ -88,6 +93,9 @@ extern "C"
|
|||||||
rc = i2cInitialize();
|
rc = i2cInitialize();
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
||||||
|
rc = appletInitialize();
|
||||||
|
if (R_FAILED(rc))
|
||||||
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
||||||
}
|
}
|
||||||
|
|
||||||
void __appExit(void)
|
void __appExit(void)
|
||||||
@@ -97,6 +105,7 @@ extern "C"
|
|||||||
i2cExit();
|
i2cExit();
|
||||||
fsExit();
|
fsExit();
|
||||||
fsdevUnmountAll();
|
fsdevUnmountAll();
|
||||||
|
appletExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
assets/logo.png
BIN
assets/logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 233 KiB |
168
dist/README.md
vendored
Normal file
168
dist/README.md
vendored
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
<img src="assets/logo.png" alt="logo" width="768"/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
[](https://dsc.gg/horizonoc)
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## ⚠️ Disclaimer
|
||||||
|
|
||||||
|
> **THIS TOOL CAN BE DANGEROUS IF MISUSED. PROCEED WITH CAUTION.**
|
||||||
|
> Due to the design of Horizon OS, **overclocking RAM can cause NAND OR SD CORRUPTION.**
|
||||||
|
> Ensure you have a **full NAND, PROINFO, EMUMMC and SD backup** before proceeding.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
**Horizon OC** is an open-source overclocking tool for Nintendo Switch consoles running **Atmosphere custom firmware**.
|
||||||
|
It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration tools.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* **CPU:** Up to 1963MHz (Mariko) / 1785MHz (Erista)
|
||||||
|
* **GPU:** Up to 1075MHz (Mariko) / 921MHz (Erista)
|
||||||
|
* **RAM:** Up to 1866/2133MHz (Mariko) / 1600MHz (Erista)
|
||||||
|
* Over/undervolting support
|
||||||
|
* Built-in configurator
|
||||||
|
* Compatible with most homebrew
|
||||||
|
|
||||||
|
> It is recommended to read the [guide](https://rentry.co/howtoget60fps) before proceeding, as this can help you get a *significant* performance boost over the default settings, often times with less power draw and heat output
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Ensure you have the latest versions of
|
||||||
|
|
||||||
|
* [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere)
|
||||||
|
* [Ultrahand Overlay](https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||||
|
2. Download and extract the **Horizon OC Package** to the root of your SD card.
|
||||||
|
3. If using **Hekate**, edit `hekate_ipl.ini` to include:
|
||||||
|
|
||||||
|
```
|
||||||
|
kip1=atmosphere/kips/hoc.kip
|
||||||
|
```
|
||||||
|
|
||||||
|
*(No changes needed if using fusee.)*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
1. Open the Horizon OC Overlay
|
||||||
|
2. Open the settings menu
|
||||||
|
3. Adjust your overclocking settings as desired. A helpful guide can be found [here.](https://rentry.co/mariko#oc-settings-for-horizon-oc)
|
||||||
|
4. Click **Save KIP Settings** to apply your configuration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building from Source
|
||||||
|
|
||||||
|
Refer to COMPILATION.md
|
||||||
|
|
||||||
|
---
|
||||||
|
## Clock table
|
||||||
|
|
||||||
|
### MEM clocks
|
||||||
|
* 3200 → max on mariko, JEDEC.
|
||||||
|
* 2933 → JEDEC.
|
||||||
|
* 2666 → JEDEC.
|
||||||
|
* 2400 → max on erista, JEDEC.
|
||||||
|
* 2133 → mariko safe max (4266 Modules), JEDEC.
|
||||||
|
* 1996 → JEDEC.
|
||||||
|
* 1866 → mariko safe max (3733 Modules), JEDEC.
|
||||||
|
* 1600 → official docked, boost mode, erista safe max, JEDEC.
|
||||||
|
* 1331 → official handheld, JEDEC.
|
||||||
|
* 1065
|
||||||
|
* 800
|
||||||
|
* 665
|
||||||
|
|
||||||
|
### CPU clocks
|
||||||
|
* 2601 → mariko absolute max, very dangerous
|
||||||
|
* 2499
|
||||||
|
* 2397 → mariko safe max with UV (low speedo)
|
||||||
|
* 2295
|
||||||
|
* 2193
|
||||||
|
* 2091
|
||||||
|
* 1963 → mariko no UV max clock
|
||||||
|
* 1887
|
||||||
|
* 1785 → erista no UV max clock, boost mode
|
||||||
|
* 1683
|
||||||
|
* 1581
|
||||||
|
* 1428
|
||||||
|
* 1326
|
||||||
|
* 1224 → sdev oc
|
||||||
|
* 1122
|
||||||
|
* 1020 → official docked & handheld
|
||||||
|
* 918
|
||||||
|
* 816
|
||||||
|
* 714
|
||||||
|
* 612 → sleep mode
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
1. On Erista, CPU in handheld is capped to 1581MHz
|
||||||
|
|
||||||
|
### GPU clocks
|
||||||
|
* 1536 → absolute max clock on mariko. very dangerous
|
||||||
|
* 1459
|
||||||
|
* 1382
|
||||||
|
* 1305
|
||||||
|
* 1267 → NVIDIA T214 rating
|
||||||
|
* 1228 → mariko HiOPT safe clock
|
||||||
|
* 1152 → mariko SLT max clock
|
||||||
|
* 1075 → mariko no UV max clock. absolute max clock on erista. very dangerous
|
||||||
|
* 998 → NVIDIA T210 rating
|
||||||
|
* 960 (erista only) → erista slt/hiopt safe max clock
|
||||||
|
* 921 → erista no UV max clock
|
||||||
|
* 844
|
||||||
|
* 768 → official docked
|
||||||
|
* 691
|
||||||
|
* 614
|
||||||
|
* 537
|
||||||
|
* 460 → max handheld
|
||||||
|
* 384 → official handheld
|
||||||
|
* 307 → official handheld
|
||||||
|
* 230
|
||||||
|
* 153
|
||||||
|
* 76 → boost mode
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
1. GPU overclock is capped at 460MHz on erista in handheld
|
||||||
|
2. On Mariko, cap with No uv is 614MHz, with SLT it is 691MHz and with HiOPT it's 768MHz
|
||||||
|
3. Clocks higher than 768MHz on erista need the official charger is plugged in.
|
||||||
|
4. On Mariko, cap with No uv is 844MHz, with SLT it is 921MHz and with HiOPT it's 998MHz
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
* **Lightos's Cat** - Cat
|
||||||
|
|
||||||
|
* **Souldbminer** – hoc-clk and loader development
|
||||||
|
* **Lightos** – loader patches development
|
||||||
|
* **SciresM** - Atmosphere CFW
|
||||||
|
* **CTCaer** - L4T, Hekate, perfect ram timings
|
||||||
|
* **KazushiMe** – Switch OC Suite
|
||||||
|
* **hanai3bi (meha)** – Switch OC Suite, EOS, sys-clk-eos
|
||||||
|
* **NaGaa95** – L4T-OC-kernel
|
||||||
|
* **B3711 (halop)** – EOS
|
||||||
|
* **sys-clk team (m4xw, p-sam, natinusala)** – sys-clk
|
||||||
|
* **b0rd2death** – Ultrahand sys-clk & Status Monitor fork
|
||||||
|
* **MasaGratoR and ZachyCatGames** - General help
|
||||||
|
* **MasaGratoR** - Status Monitor & Display Refresh Rate Driver
|
||||||
|
* **Dom, Samybigio, Arcdelta, Miki, Happy, Flopsider, Winnerboi77, Blaise, Alvise, TDRR, agjeococh, frost, letum00 and Xenshen** - Testing
|
||||||
|
* **Samybigio2011** - Italian translations
|
||||||
BIN
dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
vendored
BIN
dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
vendored
Binary file not shown.
BIN
dist/atmosphere/kips/hoc.kip
vendored
BIN
dist/atmosphere/kips/hoc.kip
vendored
Binary file not shown.
132
dist/config/horizon-oc/lang/lang/zh-tw.json
vendored
Normal file
132
dist/config/horizon-oc/lang/lang/zh-tw.json
vendored
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"Horizon OC Zeus": "Horizon OC Zeus",
|
||||||
|
"Edit App Profile": "編輯應用配置",
|
||||||
|
"Advanced": "高級",
|
||||||
|
"Edit Global Profile": "編輯全局配置",
|
||||||
|
"Temporary Overrides": "臨時覆蓋",
|
||||||
|
"Temporary Overrides Reset": "臨時覆蓋 重設",
|
||||||
|
"Settings": "設置",
|
||||||
|
"Information": "資訊",
|
||||||
|
"Enable": "啟用",
|
||||||
|
"Uncapped Clocks": "解除頻率上限",
|
||||||
|
"Override Boost Mode": "覆蓋加速模式",
|
||||||
|
"CPU Max Display Clock": "CPU 最大顯示頻率",
|
||||||
|
"Thermal Throttle": "溫度節流",
|
||||||
|
"Thermal Throttle Threshold": "溫度節流閾值",
|
||||||
|
"Handheld TDP": "掌機模式 TDP",
|
||||||
|
"Handheld TDP Limit": "掌機模式 TDP 限制",
|
||||||
|
"Lite TDP Limit": "Lite TDP 限制",
|
||||||
|
"Enforce Board Limit": "強制主板限制",
|
||||||
|
"Battery Charge Current": "電池充電電流",
|
||||||
|
"Display Refresh Rate Changing": "顯示刷新率變更",
|
||||||
|
"Fix CPU Volt Bug": "修復 CPU 電壓錯誤",
|
||||||
|
"[cfg] no enum format string": "[cfg] 無枚舉格式字串",
|
||||||
|
"KIP": "KIP",
|
||||||
|
"Save KIP Settings": "保存 KIP 設置",
|
||||||
|
"RAM Settings": "記憶體設定",
|
||||||
|
"CPU Settings": "CPU 設置",
|
||||||
|
"GPU Settings": "GPU 設置",
|
||||||
|
"Experimental": "實驗性功能",
|
||||||
|
"Charge Current Override": "充電電流覆蓋",
|
||||||
|
"Disabled": "禁用",
|
||||||
|
"HP Mode": "高性能模式",
|
||||||
|
"EMC Max Clock": "EMC 最大頻率",
|
||||||
|
"EMC VDD2 Voltage": "EMC VDD2 電壓",
|
||||||
|
"EMC VDDQ Voltage": "EMC VDDQ 電壓",
|
||||||
|
"DVB Shift": "DVB 偏移",
|
||||||
|
"Memory Timings": "記憶體時序",
|
||||||
|
"Memory Latencies": "記憶體延遲",
|
||||||
|
"t1 tRCD": "t1 tRCD",
|
||||||
|
"t2 tRP": "t2 tRP",
|
||||||
|
"t3 tRAS": "t3 tRAS",
|
||||||
|
"t4 tRRD": "t4 tRRD",
|
||||||
|
"t5 tRFC": "t5 tRFC",
|
||||||
|
"t6 tRTW": "t6 tRTW",
|
||||||
|
"t7 tWTR": "t7 tWTR",
|
||||||
|
"t8 tREFI": "t8 tREFI",
|
||||||
|
"Update RAM Timings": "更新記憶體時序",
|
||||||
|
"\uE150 This feature is EXPERIMENTAL": "\uE150 此功能為實驗性功能",
|
||||||
|
"and should only be used for testing!": "僅應用於測試!",
|
||||||
|
"Read Latency": "讀取延遲",
|
||||||
|
"Write Latency": "寫入延遲",
|
||||||
|
"CPU UV": "CPU 降壓",
|
||||||
|
"CPU Unlock": "CPU 解鎖",
|
||||||
|
"CPU VMIN": "CPU 最低電壓",
|
||||||
|
"CPU Max Voltage": "CPU 最大電壓",
|
||||||
|
"CPU UV Table": "CPU 降壓表",
|
||||||
|
"CPU Low UV": "CPU 低頻降壓",
|
||||||
|
"CPU High UV": "CPU 高頻降壓",
|
||||||
|
"CPU Max Clock": "CPU 最大頻率",
|
||||||
|
"CPU Low VMIN": "CPU 低頻最低電壓",
|
||||||
|
"CPU High VMIN": "CPU 高頻最低電壓",
|
||||||
|
"GPU Undervolt Table": "GPU 降壓表",
|
||||||
|
"Calculate GPU Vmin": "計算 GPU 最低電壓",
|
||||||
|
"GPU VMIN": "GPU 最低電壓",
|
||||||
|
"GPU VMAX": "GPU 最大電壓",
|
||||||
|
"GPU Volt Offset": "GPU 電壓偏移",
|
||||||
|
"GPU Custom Table": "GPU 自訂表",
|
||||||
|
"GPU Custom Table (mV)": "GPU 自訂表 (mV)",
|
||||||
|
"\uE150 Setting GPU Clocks past": "\uE150 將 GPU 頻率設置超過",
|
||||||
|
"1075MHz without UV, 1152MHz on SLT or ": "無降壓時的 1075MHz、SLT 時的 1152MHz 或",
|
||||||
|
"1228MHz on HiOPT can cause ": "HiOPT 時的 1228MHz 可能會造成",
|
||||||
|
"permanent damage to your Switch!": "對您的 Switch 造成永久性損壞!",
|
||||||
|
"Proceed at your own risk!": "風險自負!",
|
||||||
|
"921MHz without UV and 960MHz on": "無降壓時的 921MHz 和",
|
||||||
|
"SLT or HiOPT can cause ": "SLT 或 HiOPT 時的 960MHz 可能會造成",
|
||||||
|
"Auto": "自動",
|
||||||
|
"Sleep Mode": "休眠模式",
|
||||||
|
"Stock": "默認",
|
||||||
|
"Dev OC": "開發超頻",
|
||||||
|
"Boost Mode": "加速模式",
|
||||||
|
"Safe Max": "安全最大值",
|
||||||
|
"Unsafe Max": "不安全最大值",
|
||||||
|
"Absolute Max": "絕對最大值",
|
||||||
|
"Boost Mode & Safe Max": "加速模式 & 安全最大值",
|
||||||
|
"Official Rating": "官方額定值",
|
||||||
|
"Default (Mariko)": "默認 (Mariko)",
|
||||||
|
"Default (Erista)": "默認 (Erista)",
|
||||||
|
"Rating": "額定值",
|
||||||
|
"Safe Max (Mariko)": "安全最大值 (Mariko)",
|
||||||
|
"Safe Max (Erista)": "安全最大值 (Erista)",
|
||||||
|
"Default": "默認",
|
||||||
|
"1581MHz Tbreak": "1581MHz Tbreak",
|
||||||
|
"1683MHz Tbreak": "1683MHz Tbreak",
|
||||||
|
"Extreme UV Table": "極限降壓表",
|
||||||
|
"No UV": "不降壓",
|
||||||
|
"SLT Table": "SLT 表",
|
||||||
|
"HiOPT Table": "HiOPT 表",
|
||||||
|
"Power": "功耗",
|
||||||
|
"Temp": "溫度",
|
||||||
|
"Voltage": "電壓",
|
||||||
|
"TDP Threshold": "TDP 閾值",
|
||||||
|
"Lite TDP Threshold": "Lite TDP 閾值",
|
||||||
|
"Thermal Throttle Limit": "溫度節流限制",
|
||||||
|
"1600BL": "1600BL",
|
||||||
|
"1866BL": "1866BL",
|
||||||
|
"2133BL": "2133BL",
|
||||||
|
"BAT": "電池",
|
||||||
|
"FAN": "風扇",
|
||||||
|
"DISP": "顯示",
|
||||||
|
"Board": "主板",
|
||||||
|
"Skin": "外殼",
|
||||||
|
"Now": "當前",
|
||||||
|
"Avg": "平均",
|
||||||
|
"App ID": "應用 ID",
|
||||||
|
"Profile": "配置",
|
||||||
|
"CPU": "CPU",
|
||||||
|
"GPU": "GPU",
|
||||||
|
"Memory": "記憶體",
|
||||||
|
"Display": "顯示",
|
||||||
|
"Governor": "調速器",
|
||||||
|
"SOC": "SOC",
|
||||||
|
"PCB": "PCB",
|
||||||
|
"PMIC": "PMIC",
|
||||||
|
"Docked": "底座模式",
|
||||||
|
"Handheld": "掌機模式",
|
||||||
|
"Charging": "充電中",
|
||||||
|
"USB Charger": "USB 充電器",
|
||||||
|
"PD Charger": "PD 充電器",
|
||||||
|
"VDD2": "VDD2",
|
||||||
|
"VDDQ": "VDDQ",
|
||||||
|
"GPU DVFS": "GPU DVFS"
|
||||||
|
}
|
||||||
BIN
dist/switch/.overlays/Horizon-OC-Monitor.ovl
vendored
BIN
dist/switch/.overlays/Horizon-OC-Monitor.ovl
vendored
Binary file not shown.
BIN
dist/switch/.overlays/horizon-oc-overlay.ovl
vendored
BIN
dist/switch/.overlays/horizon-oc-overlay.ovl
vendored
Binary file not shown.
Reference in New Issue
Block a user