finish rewrite (for old version)
This commit is contained in:
@@ -24,19 +24,19 @@ cp -vf "$ROOT_DIR/sysmodule/out/horizon-oc.nsp" "$DIST_DIR/atmosphere/contents/$
|
||||
>"$DIST_DIR/atmosphere/contents/$TITLE_ID/flags/boot2.flag"
|
||||
cp -vf "$ROOT_DIR/sysmodule/toolbox.json" "$DIST_DIR/atmosphere/contents/$TITLE_ID/toolbox.json"
|
||||
|
||||
echo "*** overlay ***"
|
||||
pushd "$ROOT_DIR/overlay"
|
||||
make -j$CORES
|
||||
popd > /dev/null
|
||||
# echo "*** overlay ***"
|
||||
# pushd "$ROOT_DIR/overlay"
|
||||
# make -j$CORES
|
||||
# popd > /dev/null
|
||||
|
||||
mkdir -p "$DIST_DIR/switch/.overlays"
|
||||
cp -vf "$ROOT_DIR/overlay/out/horizon-oc-overlay.ovl" "$DIST_DIR/switch/.overlays/horizon-oc-overlay.ovl"
|
||||
# mkdir -p "$DIST_DIR/switch/.overlays"
|
||||
# cp -vf "$ROOT_DIR/overlay/out/horizon-oc-overlay.ovl" "$DIST_DIR/switch/.overlays/horizon-oc-overlay.ovl"
|
||||
|
||||
echo "*** assets ***"
|
||||
mkdir -p "$DIST_DIR/config/horizon-oc"
|
||||
cp -vf "$ROOT_DIR/config.ini.template" "$DIST_DIR/config/horizon-oc/config.ini.template"
|
||||
cp -vf "$ROOT_DIR/../../README.md" "$DIST_DIR/README.md"
|
||||
# echo "*** assets ***"
|
||||
# mkdir -p "$DIST_DIR/config/horizon-oc"
|
||||
# cp -vf "$ROOT_DIR/config.ini.template" "$DIST_DIR/config/horizon-oc/config.ini.template"
|
||||
# cp -vf "$ROOT_DIR/../../README.md" "$DIST_DIR/README.md"
|
||||
|
||||
echo "*** lang ***"
|
||||
# echo "*** lang ***"
|
||||
|
||||
cp -r "$ROOT_DIR/overlay/lang/" "$DIST_DIR/config/horizon-oc/lang/"
|
||||
# cp -r "$ROOT_DIR/overlay/lang/" "$DIST_DIR/config/horizon-oc/lang/"
|
||||
|
||||
@@ -18,39 +18,7 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t crc32(const uint8_t *data, size_t length) {
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
crc ^= data[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
namespace crc32 {
|
||||
uint32_t crc32(const uint8_t *data, size_t length);
|
||||
uint32_t checksum_file(const char *filename);
|
||||
}
|
||||
|
||||
uint32_t checksum_file(const char *filename) {
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
perror("[crc32] Error opening file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buffer[1024];
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
size_t bytes_read;
|
||||
|
||||
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
||||
for (size_t i = 0; i < bytes_read; i++) {
|
||||
crc ^= buffer[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return ~crc;
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, based on reasearch by MasaGratoR and Cooler3D
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hSyncWidth;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vFrontPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint8_t vBackPorch;
|
||||
uint8_t VIC;
|
||||
uint32_t pixelClock_kHz;
|
||||
} DockedTimings;
|
||||
|
||||
typedef struct {
|
||||
uint8_t hSyncWidth;
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint16_t vFrontPorch;
|
||||
uint8_t vBackPorch;
|
||||
uint32_t pixelClock_kHz;
|
||||
} HandheldTimings;
|
||||
|
||||
typedef struct {
|
||||
uint8_t min;
|
||||
uint8_t max;
|
||||
} MinMaxRefreshRate;
|
||||
|
||||
typedef struct {
|
||||
uint32_t unk0;
|
||||
uint32_t hActive;
|
||||
uint32_t vActive;
|
||||
uint32_t hSyncWidth;
|
||||
uint32_t vSyncWidth;
|
||||
uint32_t hFrontPorch;
|
||||
uint32_t vFrontPorch;
|
||||
uint32_t hBackPorch;
|
||||
uint32_t vBackPorch;
|
||||
uint32_t pclkKHz;
|
||||
uint32_t bitsPerPixel;
|
||||
uint32_t vmode;
|
||||
uint32_t sync;
|
||||
uint32_t unk1;
|
||||
uint32_t reserved;
|
||||
} NvdcMode2;
|
||||
|
||||
typedef struct {
|
||||
NvdcMode2 modes[201];
|
||||
uint32_t num_modes;
|
||||
} NvdcModeDB2;
|
||||
|
||||
typedef struct {
|
||||
unsigned int PLLD_DIVM: 8;
|
||||
unsigned int reserved_1: 3;
|
||||
unsigned int PLLD_DIVN: 8;
|
||||
unsigned int reserved_2: 1;
|
||||
unsigned int PLLD_DIVP: 3;
|
||||
unsigned int CSI_CLK_SRC: 1;
|
||||
unsigned int reserved_3: 1;
|
||||
unsigned int PLL_D: 1;
|
||||
unsigned int reserved_4: 1;
|
||||
unsigned int PLLD_LOCK: 1;
|
||||
unsigned int reserved_5: 1;
|
||||
unsigned int PLLD_REF_DIS: 1;
|
||||
unsigned int PLLD_ENABLE: 1;
|
||||
unsigned int PLLD_BYPASS: 1;
|
||||
} PLLD_BASE;
|
||||
|
||||
typedef struct {
|
||||
signed int PLLD_SDM_DIN: 16;
|
||||
unsigned int PLLD_EN_SDM: 1;
|
||||
unsigned int PLLD_LOCK_OVERRIDE: 1;
|
||||
unsigned int PLLD_EN_LCKDET: 1;
|
||||
unsigned int PLLD_FREQLOCK: 1;
|
||||
unsigned int PLLD_IDDQ: 1;
|
||||
unsigned int PLLD_ENABLE_CLK: 1;
|
||||
unsigned int PLLD_KVCO: 1;
|
||||
unsigned int PLLD_KCP: 2;
|
||||
unsigned int PLLD_PTS: 2;
|
||||
unsigned int PLLD_LDPULSE_ADJ: 3;
|
||||
unsigned int reserved: 2;
|
||||
} PLLD_MISC;
|
||||
|
||||
typedef struct {
|
||||
uint64_t clkVirtAddr;
|
||||
uint64_t dsiVirtAddr;
|
||||
bool isDocked;
|
||||
bool isLite;
|
||||
bool isRetroSUPER;
|
||||
bool isPossiblySpoofedRetro;
|
||||
bool dontForce60InDocked;
|
||||
bool matchLowestDocked;
|
||||
bool displaySync;
|
||||
bool displaySyncOutOfFocus60;
|
||||
bool displaySyncDocked;
|
||||
bool displaySyncDockedOutOfFocus60;
|
||||
} DisplayRefreshConfig;
|
||||
bool DisplayRefresh_Initialize(const DisplayRefreshConfig* config);
|
||||
void DisplayRefresh_SetDockedState(bool isDocked);
|
||||
bool DisplayRefresh_SetRate(uint32_t new_refreshRate);
|
||||
bool DisplayRefresh_GetRate(uint32_t* out_refreshRate, bool internal);
|
||||
uint8_t DisplayRefresh_GetDockedHighestAllowed(void);
|
||||
void DisplayRefresh_CorrectOledGamma(uint32_t refresh_rate);
|
||||
void DisplayRefresh_SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p);
|
||||
void DisplayRefresh_Shutdown(void);
|
||||
@@ -20,25 +20,6 @@
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include <cstdio>
|
||||
|
||||
static void writeNotification(const std::string& message) {
|
||||
static const char* flagPath = "sdmc:/config/ultrahand/flags/NOTIFICATIONS.flag";
|
||||
|
||||
FILE* flagFile = fopen(flagPath, "r");
|
||||
if (!flagFile) {
|
||||
return;
|
||||
}
|
||||
fclose(flagFile);
|
||||
|
||||
std::string filename = "Horizon OC -" + std::to_string(std::time(nullptr)) + ".notify";
|
||||
std::string fullPath = "sdmc:/config/ultrahand/notifications/" + filename;
|
||||
|
||||
FILE* file = fopen(fullPath.c_str(), "w");
|
||||
if (file) {
|
||||
fprintf(file, "{\n");
|
||||
fprintf(file, " \"text\": \"%s\",\n", message.c_str());
|
||||
fprintf(file, " \"fontSize\": 28\n");
|
||||
fprintf(file, "}\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
namespace notification {
|
||||
void writeNotification(const std::string& message);
|
||||
}
|
||||
|
||||
56
Source/rewrite-hoc-clk/common/src/crc32.cpp
Normal file
56
Source/rewrite-hoc-clk/common/src/crc32.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crc32.h>
|
||||
|
||||
namespace crc32 {
|
||||
uint32_t crc32(const uint8_t *data, size_t length) {
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
crc ^= data[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
uint32_t checksum_file(const char *filename) {
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
perror("[crc32] Error opening file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buffer[1024];
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
size_t bytes_read;
|
||||
|
||||
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
||||
for (size_t i = 0; i < bytes_read; i++) {
|
||||
crc ^= buffer[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return ~crc;
|
||||
}
|
||||
}
|
||||
@@ -1,733 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, based on reasearch by MasaGratoR and Cooler3D
|
||||
*
|
||||
* 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 "display_refresh_rate.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <switch.h>
|
||||
#define DSI_CLOCK_HZ 234000000llu
|
||||
#define NVDISP_GET_MODE2 0x803C021B
|
||||
#define NVDISP_SET_MODE2 0x403C021C
|
||||
#define NVDISP_VALIDATE_MODE2 0xC03C021D
|
||||
#define NVDISP_GET_MODE_DB2 0xEF20021E
|
||||
#define NVDISP_GET_PANEL_DATA 0xC01C0226
|
||||
|
||||
#define MAX_REFRESH_RATE 72
|
||||
|
||||
static DisplayRefreshConfig g_config = {0};
|
||||
static bool g_initialized = false;
|
||||
|
||||
static uint8_t g_dockedHighestRefreshRate = 60;
|
||||
static uint8_t g_dockedLinkRate = 10;
|
||||
static bool g_wasRetroSuperTurnedOff = false;
|
||||
static uint32_t g_lastVActive = 1080;
|
||||
static bool g_canChangeRefreshRateDocked = false;
|
||||
static uint8_t g_lastVActiveSet = 0;
|
||||
|
||||
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:
|
||||
|
||||
// https://tomverbeure.github.io/video_timings_calculator?horiz_pixels=1920&vert_pixels=1080&refresh_rate=240&margins=false&interlaced=false&bpc=8&color_fmt=rgb444&video_opt=false&custom_hblank=80&custom_vblank=6
|
||||
|
||||
/*
|
||||
typedef struct {
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hSyncWidth;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vFrontPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint8_t vBackPorch;
|
||||
uint8_t VIC;
|
||||
uint32_t pixelClock_kHz;
|
||||
} DockedTimings;
|
||||
*/
|
||||
static const DockedTimings g_dockedTimings1080p[] = {
|
||||
{8, 32, 40, 7, 8, 6, 0, 88080}, // 40Hz
|
||||
{8, 32, 40, 9, 8, 6, 0, 99270}, // 45Hz
|
||||
{528, 44, 148, 4, 5, 36, 31, 148500}, // 50Hz
|
||||
{8, 32, 40, 15, 8, 6, 0, 121990}, // 55Hz
|
||||
{88, 44, 148, 4, 5, 36, 16, 148500}, // 60Hz
|
||||
{8, 32, 40, 22, 8, 6, 0, 156240}, // 70Hz
|
||||
{8, 32, 40, 23, 8, 6, 0, 160848}, // 72Hz
|
||||
{8, 32, 40, 25, 8, 6, 0, 167850}, // 75Hz
|
||||
{8, 32, 40, 28, 8, 6, 0, 179520}, // 80Hz
|
||||
{8, 32, 40, 33, 8, 6, 0, 202860}, // 90Hz
|
||||
{8, 32, 40, 36, 8, 6, 0, 214700}, // 95Hz
|
||||
{528, 44, 148, 4, 5, 36, 64, 297000}, // 100Hz
|
||||
{8, 32, 40, 44, 8, 6, 0, 250360}, // 110Hz
|
||||
{88, 44, 148, 4, 5, 36, 63, 297000}, // 120Hz
|
||||
{8, 32, 40, 55, 8, 6, 0, 298750}, //130Hz CVT-RBv2
|
||||
{8, 32, 40, 61, 8, 6, 0, 323400}, //140Hz CVT-RBv2
|
||||
{8, 32, 40, 63, 8, 6, 0, 333216}, //144Hz CVT-RBv2
|
||||
{8, 32, 40, 67, 8, 6, 0, 348300}, //150Hz CVT-RBv2
|
||||
{8, 32, 40, 72, 8, 6, 0, 373120}, //160Hz CVT-RBv2
|
||||
{8, 32, 40, 75, 8, 6, 0, 385770}, //165Hz CVT-RBv2
|
||||
{8, 32, 40, 78, 8, 6, 0, 398480}, //170Hz CVT-RBv2
|
||||
{8, 32, 40, 84, 8, 6, 0, 424080}, //180Hz CVT-RBv2
|
||||
{8, 32, 40, 90, 8, 6, 0, 449920}, //190Hz CVT-RBv2
|
||||
{8, 32, 40, 96, 8, 6, 0, 476000}, //200Hz CVT-RBv2
|
||||
{8, 32, 40, 102, 8, 6, 0, 502320}, //210Hz 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, 121, 8, 6, 0, 583200}, //240Hz CVT-RBv2
|
||||
// technically you can go to 476hz, but in practice, why would you?
|
||||
};
|
||||
|
||||
// These timings *should* work but are untested
|
||||
static const HandheldTimings g_handheldTimingsRETRO[] = {
|
||||
{72, 136, 72, 1, 660, 9, 78000}, // 40Hz
|
||||
{72, 136, 72, 1, 612, 9, 77982}, // 41Hz
|
||||
{72, 136, 72, 1, 567, 9, 77994}, // 42Hz
|
||||
{72, 136, 72, 1, 524, 9, 78002}, // 43Hz
|
||||
{72, 136, 72, 1, 483, 9, 78012}, // 44Hz
|
||||
{72, 136, 72, 1, 443, 9, 77985}, // 45Hz
|
||||
{72, 136, 72, 1, 406, 9, 78016}, // 46Hz
|
||||
{72, 136, 72, 1, 370, 9, 78020}, // 47Hz
|
||||
{72, 136, 72, 1, 335, 9, 78000}, // 48Hz
|
||||
{72, 136, 72, 1, 302, 9, 78008}, // 49Hz
|
||||
{72, 136, 72, 1, 270, 9, 78000}, // 50Hz
|
||||
{72, 136, 72, 1, 239, 9, 77979}, // 51Hz
|
||||
{72, 136, 72, 1, 210, 9, 78000}, // 52Hz
|
||||
{72, 136, 72, 1, 182, 9, 78016}, // 53Hz
|
||||
{72, 136, 72, 1, 154, 9, 77976}, // 54Hz
|
||||
{72, 136, 72, 1, 128, 9, 77990}, // 55Hz
|
||||
{72, 136, 72, 1, 103, 9, 78008}, // 56Hz
|
||||
{72, 136, 72, 1, 78, 9, 77976}, // 57Hz
|
||||
{72, 136, 72, 1, 55, 9, 78010}, // 58Hz
|
||||
{72, 136, 72, 1, 32, 9, 77998}, // 59Hz
|
||||
{72, 136, 72, 1, 10, 9, 78000}, // 60Hz
|
||||
};
|
||||
|
||||
static const MinMaxRefreshRate g_handheldModeRefreshRate = {40, 80};
|
||||
|
||||
static uint8_t _getDockedRefreshRateIterator(uint32_t refreshRate) {
|
||||
for (size_t i = 0; i < sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]); i++) {
|
||||
if (g_dockedRefreshRates[i] == refreshRate) return i;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void _changeOledElvssSettings(const uint32_t* offsets, const uint32_t* value, uint32_t size, uint32_t start) {
|
||||
if (!g_config.dsiVirtAddr || !value || !size) return;
|
||||
|
||||
volatile uint32_t* dsi = (uint32_t*)g_config.dsiVirtAddr;
|
||||
|
||||
#define DSI_VIDEO_MODE_CONTROL 0x4E
|
||||
#define DSI_WR_DATA 0xA
|
||||
#define DSI_TRIGGER 0x13
|
||||
#define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15
|
||||
#define MIPI_DSI_DCS_LONG_WRITE 0x39
|
||||
#define MIPI_DCS_PRIV_SM_SET_REG_OFFSET 0xB0
|
||||
#define MIPI_DCS_PRIV_SM_SET_ELVSS 0xB1
|
||||
|
||||
dsi[DSI_VIDEO_MODE_CONTROL] = true;
|
||||
svcSleepThread(20000000);
|
||||
|
||||
dsi[DSI_WR_DATA] = MIPI_DSI_DCS_LONG_WRITE | (5 << 8);
|
||||
dsi[DSI_WR_DATA] = 0x5A5A5AE2;
|
||||
dsi[DSI_WR_DATA] = 0x5A;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
|
||||
for (size_t i = start; i < size; i++) {
|
||||
dsi[DSI_WR_DATA] = ((MIPI_DCS_PRIV_SM_SET_REG_OFFSET | ((offsets[i] % 0x100) << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
dsi[DSI_WR_DATA] = ((MIPI_DCS_PRIV_SM_SET_ELVSS | (value[i] << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
}
|
||||
|
||||
dsi[DSI_WR_DATA] = MIPI_DSI_DCS_LONG_WRITE | (5 << 8);
|
||||
dsi[DSI_WR_DATA] = 0xA55A5AE2;
|
||||
dsi[DSI_WR_DATA] = 0xA5;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
|
||||
dsi[DSI_VIDEO_MODE_CONTROL] = false;
|
||||
svcSleepThread(20000000);
|
||||
}
|
||||
void DisplayRefresh_SetDockedState(bool isDocked) {
|
||||
g_config.isDocked = isDocked;
|
||||
}
|
||||
|
||||
bool DisplayRefresh_Initialize(const DisplayRefreshConfig* config) {
|
||||
if (!config) return false;
|
||||
|
||||
g_config = *config;
|
||||
g_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DisplayRefresh_CorrectOledGamma(uint32_t refresh_rate) {
|
||||
static uint32_t last_refresh_rate = 60;
|
||||
static int counter = 0;
|
||||
|
||||
if (g_config.isDocked || refresh_rate < 45 || refresh_rate > 60) {
|
||||
last_refresh_rate = 60;
|
||||
return;
|
||||
}
|
||||
|
||||
if (counter != 9) {
|
||||
counter++;
|
||||
return;
|
||||
}
|
||||
counter = 0;
|
||||
|
||||
uint32_t offsets[] = {0x1A, 0x24, 0x25};
|
||||
uint32_t values[] = {2, 0, 0x83};
|
||||
|
||||
if (refresh_rate == 60) {
|
||||
if (last_refresh_rate == 60) return;
|
||||
} else if (refresh_rate == 45) {
|
||||
if (last_refresh_rate == 45) return;
|
||||
uint32_t vals[] = {4, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else if (refresh_rate == 50) {
|
||||
if (last_refresh_rate == 50) return;
|
||||
uint32_t vals[] = {3, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else if (refresh_rate == 55) {
|
||||
if (last_refresh_rate == 55) return;
|
||||
uint32_t vals[] = {3, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
_changeOledElvssSettings(offsets, values, 3, 0);
|
||||
}
|
||||
last_refresh_rate = refresh_rate;
|
||||
}
|
||||
|
||||
void DisplayRefresh_SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p) {
|
||||
// Function kept for API compatibility but does nothing
|
||||
(void)refreshRates;
|
||||
(void)is720p;
|
||||
}
|
||||
|
||||
uint8_t DisplayRefresh_GetDockedHighestAllowed(void) {
|
||||
return (g_dockedHighestRefreshRate > 60) ? g_dockedHighestRefreshRate : 60;
|
||||
}
|
||||
|
||||
static void _getDockedHighestRefreshRate(uint32_t fd_in) {
|
||||
uint8_t highestRefreshRate = 60;
|
||||
uint32_t fd = fd_in;
|
||||
|
||||
if(!fd) nvOpen(&fd, "/dev/nvdisp-disp1");
|
||||
NvdcModeDB2 db2 = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE_DB2, &db2);
|
||||
|
||||
if (rc == 0) {
|
||||
for (size_t i = 0; i < db2.num_modes; i++) {
|
||||
if (db2.modes[i].hActive < 1920 || db2.modes[i].vActive < 1080)
|
||||
continue;
|
||||
|
||||
uint32_t v_total = db2.modes[i].vActive + db2.modes[i].vSyncWidth + db2.modes[i].vFrontPorch + db2.modes[i].vBackPorch;
|
||||
uint32_t h_total = db2.modes[i].hActive + db2.modes[i].hSyncWidth + db2.modes[i].hFrontPorch + db2.modes[i].hBackPorch;
|
||||
double refreshRate = round((double)(db2.modes[i].pclkKHz * 1000) / (double)(v_total * h_total));
|
||||
|
||||
if (highestRefreshRate < (uint8_t)refreshRate)
|
||||
highestRefreshRate = (uint8_t)refreshRate;
|
||||
}
|
||||
} else {
|
||||
g_dockedHighestRefreshRate = 60;
|
||||
}
|
||||
|
||||
const size_t numRates = sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]);
|
||||
if (highestRefreshRate > g_dockedRefreshRates[numRates - 1])
|
||||
highestRefreshRate = g_dockedRefreshRates[numRates - 1];
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
|
||||
struct dpaux_read_0x100 {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char downspread;
|
||||
unsigned char training_pattern;
|
||||
unsigned char lane_pattern[4];
|
||||
unsigned char unk2[8];
|
||||
} set;
|
||||
} dpaux = {6, 0x100, 0x10};
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
if (rc == 0) {
|
||||
g_dockedLinkRate = dpaux.set.link_rate;
|
||||
// if (display_b.hActive == 1920 && display_b.vActive == 1080 && highestRefreshRate > 75 && dpaux.set.link_rate < 20 && )
|
||||
// highestRefreshRate = 75;
|
||||
}
|
||||
|
||||
if (!fd_in) nvClose(fd);
|
||||
g_dockedHighestRefreshRate = highestRefreshRate;
|
||||
}
|
||||
|
||||
static bool _setPLLDHandheldRefreshRate(uint32_t new_refreshRate) {
|
||||
if (!g_config.clkVirtAddr) return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dpaux_read {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned int rev_minor : 4;
|
||||
unsigned int rev_major : 4;
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char unk2[13];
|
||||
} DPCD;
|
||||
} dpaux = {6, 0, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
if (rc != 0x75c) return false;
|
||||
|
||||
PLLD_BASE base = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&base, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
uint32_t value = ((base.PLLD_DIVN / base.PLLD_DIVM) * 10) / 4;
|
||||
if (value == 0 || value == 80) return false;
|
||||
|
||||
if (new_refreshRate > g_handheldModeRefreshRate.max) {
|
||||
new_refreshRate = g_handheldModeRefreshRate.max;
|
||||
} else if (new_refreshRate < g_handheldModeRefreshRate.min) {
|
||||
bool skip = false;
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (new_refreshRate * i == 60) {
|
||||
skip = true;
|
||||
new_refreshRate = 60;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (((new_refreshRate * i) >= g_handheldModeRefreshRate.min) && ((new_refreshRate * i) <= g_handheldModeRefreshRate.max)) {
|
||||
skip = true;
|
||||
new_refreshRate *= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skip) new_refreshRate = 60;
|
||||
}
|
||||
|
||||
uint32_t pixelClock = (9375 * ((4096 * ((2 * base.PLLD_DIVN) + 1)) + misc.PLLD_SDM_DIN)) / (8 * base.PLLD_DIVM);
|
||||
uint16_t refreshRateNow = pixelClock / (DSI_CLOCK_HZ / 60);
|
||||
|
||||
if (refreshRateNow == new_refreshRate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t base_refreshRate = new_refreshRate - (new_refreshRate % 5);
|
||||
base.PLLD_DIVN = (4 * base_refreshRate) / 10;
|
||||
base.PLLD_DIVM = 1;
|
||||
|
||||
uint64_t expected_pixel_clock = (DSI_CLOCK_HZ * new_refreshRate) / 60;
|
||||
misc.PLLD_SDM_DIN = ((8 * base.PLLD_DIVM * expected_pixel_clock) / 9375) - (4096 * ((2 * base.PLLD_DIVN) + 1));
|
||||
|
||||
memcpy((void*)(g_config.clkVirtAddr + 0xD0), &base, 4);
|
||||
memcpy((void*)(g_config.clkVirtAddr + 0xDC), &misc, 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _setNvDispDockedRefreshRate(uint32_t new_refreshRate) {
|
||||
if (g_config.isLite || !g_canChangeRefreshRateDocked)
|
||||
return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
if (rc != 0) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!((display_b.vActive == 480 && display_b.hActive == 720) ||
|
||||
(display_b.vActive == 720 && display_b.hActive == 1280) ||
|
||||
(display_b.vActive == 1080 && display_b.hActive == 1920))) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (display_b.vActive != g_lastVActiveSet) {
|
||||
g_lastVActiveSet = display_b.vActive;
|
||||
}
|
||||
|
||||
uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint32_t refreshRateNow = ((display_b.pclkKHz) * 1000 + 999) / (h_total * v_total);
|
||||
|
||||
int8_t itr = -1;
|
||||
const size_t numRates = sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]);
|
||||
|
||||
// Find closest matching refresh rate
|
||||
if ((new_refreshRate <= 60) && ((60 % new_refreshRate) == 0)) {
|
||||
itr = _getDockedRefreshRateIterator(60);
|
||||
}
|
||||
|
||||
if (itr == -1) {
|
||||
for (size_t i = 0; i < numRates; i++) {
|
||||
uint8_t val = g_dockedRefreshRates[i];
|
||||
if ((val % new_refreshRate) == 0) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itr == -1) {
|
||||
if (!g_config.matchLowestDocked) {
|
||||
itr = _getDockedRefreshRateIterator(60);
|
||||
} else {
|
||||
for (size_t i = 0; i < numRates; i++) {
|
||||
if (new_refreshRate < g_dockedRefreshRates[i]) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itr == -1) itr = _getDockedRefreshRateIterator(60);
|
||||
|
||||
// Clamp to highest allowed refresh rate
|
||||
if (g_dockedRefreshRates[itr] > g_dockedHighestRefreshRate) {
|
||||
for (int8_t i = itr; i >= 0; i--) {
|
||||
if (g_dockedRefreshRates[i] <= g_dockedHighestRefreshRate) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshRateNow == g_dockedRefreshRates[itr]) {
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itr >= 0 && itr < (int8_t)numRates) {
|
||||
if (display_b.vActive == 720) {
|
||||
uint32_t clock = ((h_total * v_total) * g_dockedRefreshRates[itr]) / 1000;
|
||||
display_b.pclkKHz = clock;
|
||||
} else {
|
||||
display_b.hFrontPorch = g_dockedTimings1080p[itr].hFrontPorch;
|
||||
display_b.hSyncWidth = g_dockedTimings1080p[itr].hSyncWidth;
|
||||
display_b.hBackPorch = g_dockedTimings1080p[itr].hBackPorch;
|
||||
display_b.vFrontPorch = g_dockedTimings1080p[itr].vFrontPorch;
|
||||
display_b.vSyncWidth = g_dockedTimings1080p[itr].vSyncWidth;
|
||||
display_b.vBackPorch = g_dockedTimings1080p[itr].vBackPorch;
|
||||
display_b.pclkKHz = g_dockedTimings1080p[itr].pixelClock_kHz;
|
||||
display_b.vmode = (g_dockedRefreshRates[itr] >= 100 ? 0x400000 : 0x200000);
|
||||
display_b.unk1 = (g_dockedRefreshRates[itr] >= 100 ? 0x80 : 0);
|
||||
display_b.sync = 3;
|
||||
display_b.bitsPerPixel = 24;
|
||||
}
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_VALIDATE_MODE2, &display_b);
|
||||
if (rc == 0) {
|
||||
rc = nvIoctl(fd, NVDISP_SET_MODE2, &display_b);
|
||||
}
|
||||
}
|
||||
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _setNvDispHandheldRefreshRate(uint32_t new_refreshRate) {
|
||||
if (!g_config.isRetroSUPER) return false;
|
||||
|
||||
if (!g_config.displaySync) {
|
||||
g_wasRetroSuperTurnedOff = false;
|
||||
} else if (g_wasRetroSuperTurnedOff) {
|
||||
svcSleepThread(2000000000);
|
||||
g_wasRetroSuperTurnedOff = false;
|
||||
}
|
||||
|
||||
svcSleepThread(1000000000);
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
if (rc != 0) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((display_b.vActive == 1280 && display_b.hActive == 720) == false) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint32_t refreshRateNow = ((display_b.pclkKHz) * 1000 + 999) / (h_total * v_total);
|
||||
|
||||
if (new_refreshRate > g_handheldModeRefreshRate.max) {
|
||||
new_refreshRate = g_handheldModeRefreshRate.max;
|
||||
} else if (new_refreshRate < g_handheldModeRefreshRate.min) {
|
||||
bool skip = false;
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (new_refreshRate * i == 60) {
|
||||
skip = true;
|
||||
new_refreshRate = 60;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
for (size_t i = 2; i <= (sizeof(g_handheldTimingsRETRO) / sizeof(g_handheldTimingsRETRO[0])); i++) {
|
||||
if (((new_refreshRate * i) >= g_handheldModeRefreshRate.min) && ((new_refreshRate * i) <= g_handheldModeRefreshRate.max)) {
|
||||
skip = true;
|
||||
new_refreshRate *= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skip) new_refreshRate = 60;
|
||||
}
|
||||
|
||||
if (new_refreshRate == refreshRateNow) {
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t itr = (new_refreshRate - 40) / 5;
|
||||
display_b.hFrontPorch = g_handheldTimingsRETRO[itr].hFrontPorch;
|
||||
display_b.hSyncWidth = g_handheldTimingsRETRO[itr].hSyncWidth;
|
||||
display_b.hBackPorch = g_handheldTimingsRETRO[itr].hBackPorch;
|
||||
display_b.vFrontPorch = g_handheldTimingsRETRO[itr].vFrontPorch;
|
||||
display_b.vSyncWidth = g_handheldTimingsRETRO[itr].vSyncWidth;
|
||||
display_b.vBackPorch = g_handheldTimingsRETRO[itr].vBackPorch;
|
||||
display_b.pclkKHz = g_handheldTimingsRETRO[itr].pixelClock_kHz;
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_VALIDATE_MODE2, &display_b);
|
||||
if (rc == 0) {
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
nvIoctl(fd, NVDISP_SET_MODE2, &display_b);
|
||||
}
|
||||
}
|
||||
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayRefresh_SetRate(uint32_t new_refreshRate) {
|
||||
if (!new_refreshRate || !g_initialized) return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
|
||||
if (g_config.isRetroSUPER && !g_config.isDocked) {
|
||||
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
||||
}
|
||||
|
||||
else if ((!g_config.isRetroSUPER && g_config.isLite) || R_FAILED(nvOpen(&fd, "/dev/nvdisp-disp1"))) {
|
||||
if (_setPLLDHandheldRefreshRate(new_refreshRate) == false)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
struct dpaux_read {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned int rev_minor : 4;
|
||||
unsigned int rev_major : 4;
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char unk2[13];
|
||||
} DPCD;
|
||||
} dpaux = {6, 0, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
|
||||
if (rc != 0) {
|
||||
if (!g_config.isRetroSUPER) {
|
||||
return _setPLLDHandheldRefreshRate(new_refreshRate);
|
||||
} else {
|
||||
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
||||
}
|
||||
} else {
|
||||
if(g_config.isDocked)
|
||||
return _setNvDispDockedRefreshRate(new_refreshRate);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DisplayRefresh_GetRate(uint32_t* out_refreshRate, bool internal) {
|
||||
if (!out_refreshRate || !g_initialized || !g_config.clkVirtAddr) return false;
|
||||
static uint32_t value = 60;
|
||||
|
||||
if (g_config.isRetroSUPER && !g_config.isDocked) {
|
||||
uint32_t fd = 0;
|
||||
PLLD_BASE temp = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&temp, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
value = ((temp.PLLD_DIVN / temp.PLLD_DIVM) * 10) / 4;
|
||||
|
||||
if (value != 0 && value != 80) {
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
NvdcMode2 display_b = {0};
|
||||
if (nvIoctl(fd, NVDISP_GET_MODE2, &display_b) == 0) {
|
||||
uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint32_t pixelClock = display_b.pclkKHz * 1000 + 999;
|
||||
value = pixelClock / (h_total * v_total);
|
||||
}
|
||||
nvClose(fd);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
g_wasRetroSuperTurnedOff = true;
|
||||
}
|
||||
}
|
||||
else if ((!g_config.isPossiblySpoofedRetro) || (g_config.isPossiblySpoofedRetro && !g_config.isRetroSUPER)) {
|
||||
PLLD_BASE temp = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&temp, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
value = ((temp.PLLD_DIVN / temp.PLLD_DIVM) * 10) / 4;
|
||||
|
||||
if (value == 0 || value == 80) {
|
||||
// Docked mode
|
||||
if (g_config.isLite) return false;
|
||||
|
||||
g_config.isDocked = true;
|
||||
|
||||
if (!g_canChangeRefreshRateDocked) {
|
||||
uint32_t fd = 0;
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
struct dpaux_read_0x100 {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char downspread;
|
||||
unsigned char training_pattern;
|
||||
unsigned char lane_pattern[4];
|
||||
unsigned char unk2[8];
|
||||
} set;
|
||||
} dpaux = {6, 0x100, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
|
||||
if (rc == 0) {
|
||||
_getDockedHighestRefreshRate(0);
|
||||
g_canChangeRefreshRateDocked = true;
|
||||
} else {
|
||||
svcSleepThread(1000000000);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(internal) {
|
||||
*out_refreshRate = value;
|
||||
return true;
|
||||
}
|
||||
uint32_t fd = 0;
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
NvdcMode2 display_b = {0};
|
||||
if (nvIoctl(fd, NVDISP_GET_MODE2, &display_b) == 0) {
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_lastVActive != display_b.vActive) {
|
||||
g_lastVActive = display_b.vActive;
|
||||
_getDockedHighestRefreshRate(fd);
|
||||
}
|
||||
|
||||
uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint32_t pixelClock = display_b.pclkKHz * 1000 + 999;
|
||||
value = pixelClock / (h_total * v_total);
|
||||
} else {
|
||||
value = 60;
|
||||
}
|
||||
nvClose(fd);
|
||||
} else {
|
||||
value = 60;
|
||||
}
|
||||
}
|
||||
else if (!g_config.isRetroSUPER) {
|
||||
// Handheld mode
|
||||
g_config.isDocked = false;
|
||||
g_canChangeRefreshRateDocked = false;
|
||||
|
||||
uint32_t pixelClock = (9375 * ((4096 * ((2 * temp.PLLD_DIVN) + 1)) + misc.PLLD_SDM_DIN)) / (8 * temp.PLLD_DIVM);
|
||||
value = pixelClock / (DSI_CLOCK_HZ / 60);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*out_refreshRate = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DisplayRefresh_Shutdown(void) {
|
||||
g_initialized = false;
|
||||
memset(&g_config, 0, sizeof(g_config));
|
||||
}
|
||||
43
Source/rewrite-hoc-clk/common/src/notification.cpp
Normal file
43
Source/rewrite-hoc-clk/common/src/notification.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) ppkantorski (bord2death)
|
||||
*
|
||||
* 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 "notification.h"
|
||||
|
||||
namespace notification {
|
||||
void writeNotification(const std::string& message) {
|
||||
static const char* flagPath = "sdmc:/config/ultrahand/flags/NOTIFICATIONS.flag";
|
||||
|
||||
FILE* flagFile = fopen(flagPath, "r");
|
||||
if (!flagFile) {
|
||||
return;
|
||||
}
|
||||
fclose(flagFile);
|
||||
|
||||
std::string filename = "Horizon OC -" + std::to_string(std::time(nullptr)) + ".notify";
|
||||
std::string fullPath = "sdmc:/config/ultrahand/notifications/" + filename;
|
||||
|
||||
FILE* file = fopen(fullPath.c_str(), "w");
|
||||
if (file) {
|
||||
fprintf(file, "{\n");
|
||||
fprintf(file, " \"text\": \"%s\",\n", message.c_str());
|
||||
fprintf(file, " \"fontSize\": 28\n");
|
||||
fprintf(file, "}\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <pwm.h>
|
||||
#include <registers.h>
|
||||
#include <battery.h>
|
||||
#include <display_refresh_rate.h>
|
||||
#include "display_refresh_rate.hpp"
|
||||
#include <rgltr.h>
|
||||
#include <notification.h>
|
||||
|
||||
@@ -141,18 +141,16 @@ namespace board {
|
||||
|
||||
StartMiscThread(pwmCheck, &iCon);
|
||||
|
||||
if (gConsoleType != HorizonOCConsoleType_Hoag) {
|
||||
u64 clkVirtAddr, dsiVirtAddr, outsize;
|
||||
u64 clkVirtAddr, dsiVirtAddr, outsize;
|
||||
|
||||
rc = svcQueryMemoryMapping(&clkVirtAddr, &outsize, 0x60006000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (clk)");
|
||||
rc = svcQueryMemoryMapping(&clkVirtAddr, &outsize, 0x60006000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (clk)");
|
||||
|
||||
rc = svcQueryMemoryMapping(&dsiVirtAddr, &outsize, 0x54300000, 0x40000);
|
||||
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (dsi)");
|
||||
rc = svcQueryMemoryMapping(&dsiVirtAddr, &outsize, 0x54300000, 0x40000);
|
||||
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (dsi)");
|
||||
|
||||
DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr};
|
||||
DisplayRefresh_Initialize(&cfg);
|
||||
}
|
||||
display::DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr};
|
||||
display::Initialize(&cfg);
|
||||
|
||||
CacheDfllData();
|
||||
}
|
||||
@@ -185,9 +183,7 @@ namespace board {
|
||||
pmdmntExit();
|
||||
nvExit();
|
||||
|
||||
if (gConsoleType != HorizonOCConsoleType_Hoag) {
|
||||
DisplayRefresh_Shutdown();
|
||||
}
|
||||
display::Shutdown();
|
||||
}
|
||||
|
||||
SysClkSocType GetSocType() {
|
||||
@@ -209,7 +205,7 @@ namespace board {
|
||||
svcCallSecureMonitor(&args);
|
||||
|
||||
if (args.X[1] == (MC_REGISTER_BASE + MC_EMEM_CFG_0)) { // if param 1 is identical read failed
|
||||
writeNotification("Horizon OC\nSecmon read failed!\n This may be a hardware issue!");
|
||||
notification::writeNotification("Horizon OC\nSecmon read failed!\n This may be a hardware issue!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -219,7 +215,7 @@ namespace board {
|
||||
/* TODO: Put this into a different file. */
|
||||
void SetDisplayRefreshDockedState(bool docked) {
|
||||
if (GetConsoleType() != HorizonOCConsoleType_Hoag) {
|
||||
DisplayRefresh_SetDockedState(docked);
|
||||
display::SetDockedState(docked);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
#include <switch.h>
|
||||
#include <sysclk.h>
|
||||
#include <nxExt.h>
|
||||
#include <display_refresh_rate.h>
|
||||
#include "display_refresh_rate.hpp"
|
||||
#include "board.hpp"
|
||||
#include "board_name.hpp"
|
||||
#include "../errors.h"
|
||||
#include "../errors.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
@@ -70,8 +70,8 @@ namespace board {
|
||||
bool usesGovenor = module > SysClkModule_MEM;
|
||||
|
||||
|
||||
if (module == HorizonOCModule_Display && HorizonOCConsoleType() != HorizonOCConsoleType_Hoag) {
|
||||
DisplayRefresh_SetRate(hz);
|
||||
if (module == HorizonOCModule_Display) {
|
||||
display::SetRate(hz);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -103,11 +103,8 @@ namespace board {
|
||||
}
|
||||
|
||||
u32 GetDisplayRate(u32 hz) {
|
||||
if (GetConsoleType() != HorizonOCConsoleType_Hoag) {
|
||||
DisplayRefresh_GetRate(&hz, false);
|
||||
return hz;
|
||||
}
|
||||
return 60;
|
||||
display::GetRate(&hz, false);
|
||||
return hz;
|
||||
}
|
||||
|
||||
u32 GetHz(SysClkModule module) {
|
||||
@@ -186,7 +183,7 @@ namespace board {
|
||||
|
||||
u32 GetHighestDockedDisplayRate() {
|
||||
if (GetConsoleType() != HorizonOCConsoleType_Hoag) {
|
||||
return DisplayRefresh_GetDockedHighestAllowed();
|
||||
return display::GetDockedHighestAllowed();
|
||||
}
|
||||
|
||||
return 60;
|
||||
@@ -226,7 +223,7 @@ namespace board {
|
||||
|
||||
void ResetToStockDisplay() {
|
||||
if (GetConsoleType() != HorizonOCConsoleType_Hoag) {
|
||||
DisplayRefresh_SetRate(60);
|
||||
display::SetRate(60);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <switch.h>
|
||||
#include <sysclk.h>
|
||||
#include <nxExt.h>
|
||||
#include "../errors.h"
|
||||
#include "../errors.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
namespace board {
|
||||
|
||||
u32 GetTemperatureMilli(SysClkThermalSensor sensor) {
|
||||
s32 GetTemperatureMilli(SysClkThermalSensor sensor) {
|
||||
s32 millis = 0;
|
||||
BatteryChargeInfo info;
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace board {
|
||||
return std::max(0, millis);
|
||||
}
|
||||
|
||||
u32 GetPowerMw(SysClkPowerSensor sensor) {
|
||||
s32 GetPowerMw(SysClkPowerSensor sensor) {
|
||||
switch (sensor) {
|
||||
case SysClkPowerSensor_Now:
|
||||
return max17050PowerNow();
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
namespace board {
|
||||
|
||||
u32 GetTemperatureMilli(SysClkThermalSensor sensor);
|
||||
u32 GetPowerMw(SysClkPowerSensor sensor);
|
||||
s32 GetTemperatureMilli(SysClkThermalSensor sensor);
|
||||
s32 GetPowerMw(SysClkPowerSensor sensor);
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "board.hpp"
|
||||
#include "board_freq.hpp"
|
||||
#include "board_volt.hpp"
|
||||
#include "../file_utils.h"
|
||||
#include "../file_utils.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
@@ -336,7 +336,7 @@ namespace board {
|
||||
|
||||
Handle handle = GetPcvHandle();
|
||||
if (handle == INVALID_HANDLE) {
|
||||
FileUtils::LogLine("[dvfs] Invalid handle!");
|
||||
fileUtils::LogLine("[dvfs] Invalid handle!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ namespace board {
|
||||
|
||||
if (R_FAILED(resultProcessMemory) || !address) {
|
||||
svcCloseHandle(handle);
|
||||
FileUtils::LogLine("[dvfs] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory));
|
||||
fileUtils::LogLine("[dvfs] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory));
|
||||
handle = INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
@@ -415,7 +415,7 @@ namespace board {
|
||||
|
||||
Handle handle = GetPcvHandle();
|
||||
if (handle == INVALID_HANDLE) {
|
||||
FileUtils::LogLine("Invalid handle!");
|
||||
fileUtils::LogLine("Invalid handle!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ namespace board {
|
||||
}
|
||||
|
||||
svcCloseHandle(handle);
|
||||
FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
|
||||
fileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
|
||||
}
|
||||
|
||||
u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket) {
|
||||
|
||||
@@ -0,0 +1,736 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, based on reasearch by MasaGratoR and Cooler3D
|
||||
*
|
||||
* 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 "display_refresh_rate.hpp"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <switch.h>
|
||||
|
||||
namespace display {
|
||||
#define DSI_CLOCK_HZ 234000000llu
|
||||
#define NVDISP_GET_MODE2 0x803C021B
|
||||
#define NVDISP_SET_MODE2 0x403C021C
|
||||
#define NVDISP_VALIDATE_MODE2 0xC03C021D
|
||||
#define NVDISP_GET_MODE_DB2 0xEF20021E
|
||||
#define NVDISP_GET_PANEL_DATA 0xC01C0226
|
||||
|
||||
#define MAX_REFRESH_RATE 72
|
||||
|
||||
static DisplayRefreshConfig g_config = {0};
|
||||
static bool g_initialized = false;
|
||||
|
||||
static uint8_t g_dockedHighestRefreshRate = 60;
|
||||
static uint8_t g_dockedLinkRate = 10;
|
||||
static bool g_wasRetroSuperTurnedOff = false;
|
||||
static uint32_t g_lastVActive = 1080;
|
||||
static bool g_canChangeRefreshRateDocked = false;
|
||||
static uint8_t g_lastVActiveSet = 0;
|
||||
|
||||
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:
|
||||
|
||||
// https://tomverbeure.github.io/video_timings_calculator?horiz_pixels=1920&vert_pixels=1080&refresh_rate=240&margins=false&interlaced=false&bpc=8&color_fmt=rgb444&video_opt=false&custom_hblank=80&custom_vblank=6
|
||||
|
||||
/*
|
||||
typedef struct {
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hSyncWidth;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vFrontPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint8_t vBackPorch;
|
||||
uint8_t VIC;
|
||||
uint32_t pixelClock_kHz;
|
||||
} DockedTimings;
|
||||
*/
|
||||
static const DockedTimings g_dockedTimings1080p[] = {
|
||||
{8, 32, 40, 7, 8, 6, 0, 88080}, // 40Hz
|
||||
{8, 32, 40, 9, 8, 6, 0, 99270}, // 45Hz
|
||||
{528, 44, 148, 4, 5, 36, 31, 148500}, // 50Hz
|
||||
{8, 32, 40, 15, 8, 6, 0, 121990}, // 55Hz
|
||||
{88, 44, 148, 4, 5, 36, 16, 148500}, // 60Hz
|
||||
{8, 32, 40, 22, 8, 6, 0, 156240}, // 70Hz
|
||||
{8, 32, 40, 23, 8, 6, 0, 160848}, // 72Hz
|
||||
{8, 32, 40, 25, 8, 6, 0, 167850}, // 75Hz
|
||||
{8, 32, 40, 28, 8, 6, 0, 179520}, // 80Hz
|
||||
{8, 32, 40, 33, 8, 6, 0, 202860}, // 90Hz
|
||||
{8, 32, 40, 36, 8, 6, 0, 214700}, // 95Hz
|
||||
{528, 44, 148, 4, 5, 36, 64, 297000}, // 100Hz
|
||||
{8, 32, 40, 44, 8, 6, 0, 250360}, // 110Hz
|
||||
{88, 44, 148, 4, 5, 36, 63, 297000}, // 120Hz
|
||||
{8, 32, 40, 55, 8, 6, 0, 298750}, //130Hz CVT-RBv2
|
||||
{8, 32, 40, 61, 8, 6, 0, 323400}, //140Hz CVT-RBv2
|
||||
{8, 32, 40, 63, 8, 6, 0, 333216}, //144Hz CVT-RBv2
|
||||
{8, 32, 40, 67, 8, 6, 0, 348300}, //150Hz CVT-RBv2
|
||||
{8, 32, 40, 72, 8, 6, 0, 373120}, //160Hz CVT-RBv2
|
||||
{8, 32, 40, 75, 8, 6, 0, 385770}, //165Hz CVT-RBv2
|
||||
{8, 32, 40, 78, 8, 6, 0, 398480}, //170Hz CVT-RBv2
|
||||
{8, 32, 40, 84, 8, 6, 0, 424080}, //180Hz CVT-RBv2
|
||||
{8, 32, 40, 90, 8, 6, 0, 449920}, //190Hz CVT-RBv2
|
||||
{8, 32, 40, 96, 8, 6, 0, 476000}, //200Hz CVT-RBv2
|
||||
{8, 32, 40, 102, 8, 6, 0, 502320}, //210Hz 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, 121, 8, 6, 0, 583200}, //240Hz CVT-RBv2
|
||||
// technically you can go to 476hz, but in practice, why would you?
|
||||
};
|
||||
|
||||
// These timings *should* work but are untested
|
||||
static const HandheldTimings g_handheldTimingsRETRO[] = {
|
||||
{72, 136, 72, 1, 660, 9, 78000}, // 40Hz
|
||||
{72, 136, 72, 1, 612, 9, 77982}, // 41Hz
|
||||
{72, 136, 72, 1, 567, 9, 77994}, // 42Hz
|
||||
{72, 136, 72, 1, 524, 9, 78002}, // 43Hz
|
||||
{72, 136, 72, 1, 483, 9, 78012}, // 44Hz
|
||||
{72, 136, 72, 1, 443, 9, 77985}, // 45Hz
|
||||
{72, 136, 72, 1, 406, 9, 78016}, // 46Hz
|
||||
{72, 136, 72, 1, 370, 9, 78020}, // 47Hz
|
||||
{72, 136, 72, 1, 335, 9, 78000}, // 48Hz
|
||||
{72, 136, 72, 1, 302, 9, 78008}, // 49Hz
|
||||
{72, 136, 72, 1, 270, 9, 78000}, // 50Hz
|
||||
{72, 136, 72, 1, 239, 9, 77979}, // 51Hz
|
||||
{72, 136, 72, 1, 210, 9, 78000}, // 52Hz
|
||||
{72, 136, 72, 1, 182, 9, 78016}, // 53Hz
|
||||
{72, 136, 72, 1, 154, 9, 77976}, // 54Hz
|
||||
{72, 136, 72, 1, 128, 9, 77990}, // 55Hz
|
||||
{72, 136, 72, 1, 103, 9, 78008}, // 56Hz
|
||||
{72, 136, 72, 1, 78, 9, 77976}, // 57Hz
|
||||
{72, 136, 72, 1, 55, 9, 78010}, // 58Hz
|
||||
{72, 136, 72, 1, 32, 9, 77998}, // 59Hz
|
||||
{72, 136, 72, 1, 10, 9, 78000}, // 60Hz
|
||||
};
|
||||
|
||||
static const MinMaxRefreshRate g_handheldModeRefreshRate = {40, 80};
|
||||
|
||||
static uint8_t _getDockedRefreshRateIterator(uint32_t refreshRate) {
|
||||
for (size_t i = 0; i < sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]); i++) {
|
||||
if (g_dockedRefreshRates[i] == refreshRate) return i;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void _changeOledElvssSettings(const uint32_t* offsets, const uint32_t* value, uint32_t size, uint32_t start) {
|
||||
if (!g_config.dsiVirtAddr || !value || !size) return;
|
||||
|
||||
volatile uint32_t* dsi = (uint32_t*)g_config.dsiVirtAddr;
|
||||
|
||||
#define DSI_VIDEO_MODE_CONTROL 0x4E
|
||||
#define DSI_WR_DATA 0xA
|
||||
#define DSI_TRIGGER 0x13
|
||||
#define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15
|
||||
#define MIPI_DSI_DCS_LONG_WRITE 0x39
|
||||
#define MIPI_DCS_PRIV_SM_SET_REG_OFFSET 0xB0
|
||||
#define MIPI_DCS_PRIV_SM_SET_ELVSS 0xB1
|
||||
|
||||
dsi[DSI_VIDEO_MODE_CONTROL] = true;
|
||||
svcSleepThread(20000000);
|
||||
|
||||
dsi[DSI_WR_DATA] = MIPI_DSI_DCS_LONG_WRITE | (5 << 8);
|
||||
dsi[DSI_WR_DATA] = 0x5A5A5AE2;
|
||||
dsi[DSI_WR_DATA] = 0x5A;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
|
||||
for (size_t i = start; i < size; i++) {
|
||||
dsi[DSI_WR_DATA] = ((MIPI_DCS_PRIV_SM_SET_REG_OFFSET | ((offsets[i] % 0x100) << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
dsi[DSI_WR_DATA] = ((MIPI_DCS_PRIV_SM_SET_ELVSS | (value[i] << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
}
|
||||
|
||||
dsi[DSI_WR_DATA] = MIPI_DSI_DCS_LONG_WRITE | (5 << 8);
|
||||
dsi[DSI_WR_DATA] = 0xA55A5AE2;
|
||||
dsi[DSI_WR_DATA] = 0xA5;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
|
||||
dsi[DSI_VIDEO_MODE_CONTROL] = false;
|
||||
svcSleepThread(20000000);
|
||||
}
|
||||
void SetDockedState(bool isDocked) {
|
||||
g_config.isDocked = isDocked;
|
||||
}
|
||||
|
||||
bool Initialize(const DisplayRefreshConfig* config) {
|
||||
if (!config) return false;
|
||||
|
||||
g_config = *config;
|
||||
g_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CorrectOledGamma(uint32_t refresh_rate) {
|
||||
static uint32_t last_refresh_rate = 60;
|
||||
static int counter = 0;
|
||||
|
||||
if (g_config.isDocked || refresh_rate < 45 || refresh_rate > 60) {
|
||||
last_refresh_rate = 60;
|
||||
return;
|
||||
}
|
||||
|
||||
if (counter != 9) {
|
||||
counter++;
|
||||
return;
|
||||
}
|
||||
counter = 0;
|
||||
|
||||
uint32_t offsets[] = {0x1A, 0x24, 0x25};
|
||||
uint32_t values[] = {2, 0, 0x83};
|
||||
|
||||
if (refresh_rate == 60) {
|
||||
if (last_refresh_rate == 60) return;
|
||||
} else if (refresh_rate == 45) {
|
||||
if (last_refresh_rate == 45) return;
|
||||
uint32_t vals[] = {4, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else if (refresh_rate == 50) {
|
||||
if (last_refresh_rate == 50) return;
|
||||
uint32_t vals[] = {3, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else if (refresh_rate == 55) {
|
||||
if (last_refresh_rate == 55) return;
|
||||
uint32_t vals[] = {3, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
_changeOledElvssSettings(offsets, values, 3, 0);
|
||||
}
|
||||
last_refresh_rate = refresh_rate;
|
||||
}
|
||||
|
||||
void SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p) {
|
||||
// Function kept for API compatibility but does nothing
|
||||
(void)refreshRates;
|
||||
(void)is720p;
|
||||
}
|
||||
|
||||
uint8_t GetDockedHighestAllowed(void) {
|
||||
return (g_dockedHighestRefreshRate > 60) ? g_dockedHighestRefreshRate : 60;
|
||||
}
|
||||
|
||||
static void _getDockedHighestRefreshRate(uint32_t fd_in) {
|
||||
uint8_t highestRefreshRate = 60;
|
||||
uint32_t fd = fd_in;
|
||||
|
||||
if(!fd) nvOpen(&fd, "/dev/nvdisp-disp1");
|
||||
NvdcModeDB2 db2 = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE_DB2, &db2);
|
||||
|
||||
if (rc == 0) {
|
||||
for (size_t i = 0; i < db2.num_modes; i++) {
|
||||
if (db2.modes[i].hActive < 1920 || db2.modes[i].vActive < 1080)
|
||||
continue;
|
||||
|
||||
uint32_t v_total = db2.modes[i].vActive + db2.modes[i].vSyncWidth + db2.modes[i].vFrontPorch + db2.modes[i].vBackPorch;
|
||||
uint32_t h_total = db2.modes[i].hActive + db2.modes[i].hSyncWidth + db2.modes[i].hFrontPorch + db2.modes[i].hBackPorch;
|
||||
double refreshRate = round((double)(db2.modes[i].pclkKHz * 1000) / (double)(v_total * h_total));
|
||||
|
||||
if (highestRefreshRate < (uint8_t)refreshRate)
|
||||
highestRefreshRate = (uint8_t)refreshRate;
|
||||
}
|
||||
} else {
|
||||
g_dockedHighestRefreshRate = 60;
|
||||
}
|
||||
|
||||
const size_t numRates = sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]);
|
||||
if (highestRefreshRate > g_dockedRefreshRates[numRates - 1])
|
||||
highestRefreshRate = g_dockedRefreshRates[numRates - 1];
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
|
||||
struct dpaux_read_0x100 {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char downspread;
|
||||
unsigned char training_pattern;
|
||||
unsigned char lane_pattern[4];
|
||||
unsigned char unk2[8];
|
||||
} set;
|
||||
} dpaux = {6, 0x100, 0x10};
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
if (rc == 0) {
|
||||
g_dockedLinkRate = dpaux.set.link_rate;
|
||||
// if (display_b.hActive == 1920 && display_b.vActive == 1080 && highestRefreshRate > 75 && dpaux.set.link_rate < 20 && )
|
||||
// highestRefreshRate = 75;
|
||||
}
|
||||
|
||||
if (!fd_in) nvClose(fd);
|
||||
g_dockedHighestRefreshRate = highestRefreshRate;
|
||||
}
|
||||
|
||||
static bool _setPLLDHandheldRefreshRate(uint32_t new_refreshRate) {
|
||||
if (!g_config.clkVirtAddr) return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dpaux_read {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned int rev_minor : 4;
|
||||
unsigned int rev_major : 4;
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char unk2[13];
|
||||
} DPCD;
|
||||
} dpaux = {6, 0, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
if (rc != 0x75c) return false;
|
||||
|
||||
PLLD_BASE base = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&base, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
uint32_t value = ((base.PLLD_DIVN / base.PLLD_DIVM) * 10) / 4;
|
||||
if (value == 0 || value == 80) return false;
|
||||
|
||||
if (new_refreshRate > g_handheldModeRefreshRate.max) {
|
||||
new_refreshRate = g_handheldModeRefreshRate.max;
|
||||
} else if (new_refreshRate < g_handheldModeRefreshRate.min) {
|
||||
bool skip = false;
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (new_refreshRate * i == 60) {
|
||||
skip = true;
|
||||
new_refreshRate = 60;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (((new_refreshRate * i) >= g_handheldModeRefreshRate.min) && ((new_refreshRate * i) <= g_handheldModeRefreshRate.max)) {
|
||||
skip = true;
|
||||
new_refreshRate *= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skip) new_refreshRate = 60;
|
||||
}
|
||||
|
||||
uint32_t pixelClock = (9375 * ((4096 * ((2 * base.PLLD_DIVN) + 1)) + misc.PLLD_SDM_DIN)) / (8 * base.PLLD_DIVM);
|
||||
uint16_t refreshRateNow = pixelClock / (DSI_CLOCK_HZ / 60);
|
||||
|
||||
if (refreshRateNow == new_refreshRate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t base_refreshRate = new_refreshRate - (new_refreshRate % 5);
|
||||
base.PLLD_DIVN = (4 * base_refreshRate) / 10;
|
||||
base.PLLD_DIVM = 1;
|
||||
|
||||
uint64_t expected_pixel_clock = (DSI_CLOCK_HZ * new_refreshRate) / 60;
|
||||
misc.PLLD_SDM_DIN = ((8 * base.PLLD_DIVM * expected_pixel_clock) / 9375) - (4096 * ((2 * base.PLLD_DIVN) + 1));
|
||||
|
||||
memcpy((void*)(g_config.clkVirtAddr + 0xD0), &base, 4);
|
||||
memcpy((void*)(g_config.clkVirtAddr + 0xDC), &misc, 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _setNvDispDockedRefreshRate(uint32_t new_refreshRate) {
|
||||
if (g_config.isLite || !g_canChangeRefreshRateDocked)
|
||||
return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
if (rc != 0) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!((display_b.vActive == 480 && display_b.hActive == 720) ||
|
||||
(display_b.vActive == 720 && display_b.hActive == 1280) ||
|
||||
(display_b.vActive == 1080 && display_b.hActive == 1920))) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (display_b.vActive != g_lastVActiveSet) {
|
||||
g_lastVActiveSet = display_b.vActive;
|
||||
}
|
||||
|
||||
uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint32_t refreshRateNow = ((display_b.pclkKHz) * 1000 + 999) / (h_total * v_total);
|
||||
|
||||
int8_t itr = -1;
|
||||
const size_t numRates = sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]);
|
||||
|
||||
// Find closest matching refresh rate
|
||||
if ((new_refreshRate <= 60) && ((60 % new_refreshRate) == 0)) {
|
||||
itr = _getDockedRefreshRateIterator(60);
|
||||
}
|
||||
|
||||
if (itr == -1) {
|
||||
for (size_t i = 0; i < numRates; i++) {
|
||||
uint8_t val = g_dockedRefreshRates[i];
|
||||
if ((val % new_refreshRate) == 0) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itr == -1) {
|
||||
if (!g_config.matchLowestDocked) {
|
||||
itr = _getDockedRefreshRateIterator(60);
|
||||
} else {
|
||||
for (size_t i = 0; i < numRates; i++) {
|
||||
if (new_refreshRate < g_dockedRefreshRates[i]) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itr == -1) itr = _getDockedRefreshRateIterator(60);
|
||||
|
||||
// Clamp to highest allowed refresh rate
|
||||
if (g_dockedRefreshRates[itr] > g_dockedHighestRefreshRate) {
|
||||
for (int8_t i = itr; i >= 0; i--) {
|
||||
if (g_dockedRefreshRates[i] <= g_dockedHighestRefreshRate) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshRateNow == g_dockedRefreshRates[itr]) {
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itr >= 0 && itr < (int8_t)numRates) {
|
||||
if (display_b.vActive == 720) {
|
||||
uint32_t clock = ((h_total * v_total) * g_dockedRefreshRates[itr]) / 1000;
|
||||
display_b.pclkKHz = clock;
|
||||
} else {
|
||||
display_b.hFrontPorch = g_dockedTimings1080p[itr].hFrontPorch;
|
||||
display_b.hSyncWidth = g_dockedTimings1080p[itr].hSyncWidth;
|
||||
display_b.hBackPorch = g_dockedTimings1080p[itr].hBackPorch;
|
||||
display_b.vFrontPorch = g_dockedTimings1080p[itr].vFrontPorch;
|
||||
display_b.vSyncWidth = g_dockedTimings1080p[itr].vSyncWidth;
|
||||
display_b.vBackPorch = g_dockedTimings1080p[itr].vBackPorch;
|
||||
display_b.pclkKHz = g_dockedTimings1080p[itr].pixelClock_kHz;
|
||||
display_b.vmode = (g_dockedRefreshRates[itr] >= 100 ? 0x400000 : 0x200000);
|
||||
display_b.unk1 = (g_dockedRefreshRates[itr] >= 100 ? 0x80 : 0);
|
||||
display_b.sync = 3;
|
||||
display_b.bitsPerPixel = 24;
|
||||
}
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_VALIDATE_MODE2, &display_b);
|
||||
if (rc == 0) {
|
||||
rc = nvIoctl(fd, NVDISP_SET_MODE2, &display_b);
|
||||
}
|
||||
}
|
||||
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _setNvDispHandheldRefreshRate(uint32_t new_refreshRate) {
|
||||
if (!g_config.isRetroSUPER) return false;
|
||||
|
||||
if (!g_config.displaySync) {
|
||||
g_wasRetroSuperTurnedOff = false;
|
||||
} else if (g_wasRetroSuperTurnedOff) {
|
||||
svcSleepThread(2000000000);
|
||||
g_wasRetroSuperTurnedOff = false;
|
||||
}
|
||||
|
||||
svcSleepThread(1000000000);
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
if (rc != 0) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((display_b.vActive == 1280 && display_b.hActive == 720) == false) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint32_t refreshRateNow = ((display_b.pclkKHz) * 1000 + 999) / (h_total * v_total);
|
||||
|
||||
if (new_refreshRate > g_handheldModeRefreshRate.max) {
|
||||
new_refreshRate = g_handheldModeRefreshRate.max;
|
||||
} else if (new_refreshRate < g_handheldModeRefreshRate.min) {
|
||||
bool skip = false;
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (new_refreshRate * i == 60) {
|
||||
skip = true;
|
||||
new_refreshRate = 60;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
for (size_t i = 2; i <= (sizeof(g_handheldTimingsRETRO) / sizeof(g_handheldTimingsRETRO[0])); i++) {
|
||||
if (((new_refreshRate * i) >= g_handheldModeRefreshRate.min) && ((new_refreshRate * i) <= g_handheldModeRefreshRate.max)) {
|
||||
skip = true;
|
||||
new_refreshRate *= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skip) new_refreshRate = 60;
|
||||
}
|
||||
|
||||
if (new_refreshRate == refreshRateNow) {
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t itr = (new_refreshRate - 40) / 5;
|
||||
display_b.hFrontPorch = g_handheldTimingsRETRO[itr].hFrontPorch;
|
||||
display_b.hSyncWidth = g_handheldTimingsRETRO[itr].hSyncWidth;
|
||||
display_b.hBackPorch = g_handheldTimingsRETRO[itr].hBackPorch;
|
||||
display_b.vFrontPorch = g_handheldTimingsRETRO[itr].vFrontPorch;
|
||||
display_b.vSyncWidth = g_handheldTimingsRETRO[itr].vSyncWidth;
|
||||
display_b.vBackPorch = g_handheldTimingsRETRO[itr].vBackPorch;
|
||||
display_b.pclkKHz = g_handheldTimingsRETRO[itr].pixelClock_kHz;
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_VALIDATE_MODE2, &display_b);
|
||||
if (rc == 0) {
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
nvIoctl(fd, NVDISP_SET_MODE2, &display_b);
|
||||
}
|
||||
}
|
||||
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetRate(uint32_t new_refreshRate) {
|
||||
if (!new_refreshRate || !g_initialized) return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
|
||||
if (g_config.isRetroSUPER && !g_config.isDocked) {
|
||||
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
||||
}
|
||||
|
||||
else if ((!g_config.isRetroSUPER && g_config.isLite) || R_FAILED(nvOpen(&fd, "/dev/nvdisp-disp1"))) {
|
||||
if (_setPLLDHandheldRefreshRate(new_refreshRate) == false)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
struct dpaux_read {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned int rev_minor : 4;
|
||||
unsigned int rev_major : 4;
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char unk2[13];
|
||||
} DPCD;
|
||||
} dpaux = {6, 0, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
|
||||
if (rc != 0) {
|
||||
if (!g_config.isRetroSUPER) {
|
||||
return _setPLLDHandheldRefreshRate(new_refreshRate);
|
||||
} else {
|
||||
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
||||
}
|
||||
} else {
|
||||
if(g_config.isDocked)
|
||||
return _setNvDispDockedRefreshRate(new_refreshRate);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetRate(uint32_t* out_refreshRate, bool internal) {
|
||||
if (!out_refreshRate || !g_initialized || !g_config.clkVirtAddr) return false;
|
||||
static uint32_t value = 60;
|
||||
|
||||
if (g_config.isRetroSUPER && !g_config.isDocked) {
|
||||
uint32_t fd = 0;
|
||||
PLLD_BASE temp = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&temp, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
value = ((temp.PLLD_DIVN / temp.PLLD_DIVM) * 10) / 4;
|
||||
|
||||
if (value != 0 && value != 80) {
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
NvdcMode2 display_b = {0};
|
||||
if (nvIoctl(fd, NVDISP_GET_MODE2, &display_b) == 0) {
|
||||
uint64_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint64_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint64_t pixelClock = display_b.pclkKHz * 1000 + 999;
|
||||
value = (u32)(pixelClock / (h_total * v_total));
|
||||
}
|
||||
nvClose(fd);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
g_wasRetroSuperTurnedOff = true;
|
||||
}
|
||||
}
|
||||
else if ((!g_config.isPossiblySpoofedRetro) || (g_config.isPossiblySpoofedRetro && !g_config.isRetroSUPER)) {
|
||||
PLLD_BASE temp = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&temp, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
value = ((temp.PLLD_DIVN / temp.PLLD_DIVM) * 10) / 4;
|
||||
|
||||
if (value == 0 || value == 80) {
|
||||
// Docked mode
|
||||
if (g_config.isLite) return false;
|
||||
|
||||
g_config.isDocked = true;
|
||||
|
||||
if (!g_canChangeRefreshRateDocked) {
|
||||
uint32_t fd = 0;
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
struct dpaux_read_0x100 {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char downspread;
|
||||
unsigned char training_pattern;
|
||||
unsigned char lane_pattern[4];
|
||||
unsigned char unk2[8];
|
||||
} set;
|
||||
} dpaux = {6, 0x100, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
|
||||
if (rc == 0) {
|
||||
_getDockedHighestRefreshRate(0);
|
||||
g_canChangeRefreshRateDocked = true;
|
||||
} else {
|
||||
svcSleepThread(1000000000);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(internal) {
|
||||
*out_refreshRate = value;
|
||||
return true;
|
||||
}
|
||||
uint32_t fd = 0;
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
NvdcMode2 display_b = {0};
|
||||
if (nvIoctl(fd, NVDISP_GET_MODE2, &display_b) == 0) {
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_lastVActive != display_b.vActive) {
|
||||
g_lastVActive = display_b.vActive;
|
||||
_getDockedHighestRefreshRate(fd);
|
||||
}
|
||||
|
||||
uint64_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint64_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint64_t pixelClock = display_b.pclkKHz * 1000 + 999;
|
||||
value = (u32)(pixelClock / (h_total * v_total));
|
||||
} else {
|
||||
value = 60;
|
||||
}
|
||||
nvClose(fd);
|
||||
} else {
|
||||
value = 60;
|
||||
}
|
||||
}
|
||||
else if (!g_config.isRetroSUPER) {
|
||||
// Handheld mode
|
||||
g_config.isDocked = false;
|
||||
g_canChangeRefreshRateDocked = false;
|
||||
|
||||
uint32_t pixelClock = (9375ULL * ((4096 * ((2 * temp.PLLD_DIVN) + 1)) + misc.PLLD_SDM_DIN)) / (8 * temp.PLLD_DIVM);
|
||||
value = pixelClock / (DSI_CLOCK_HZ / 60);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*out_refreshRate = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown(void) {
|
||||
g_initialized = false;
|
||||
memset(&g_config, 0, sizeof(g_config));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, based on reasearch by MasaGratoR and Cooler3D
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
namespace display {
|
||||
typedef struct {
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hSyncWidth;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vFrontPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint8_t vBackPorch;
|
||||
uint8_t VIC;
|
||||
uint32_t pixelClock_kHz;
|
||||
} DockedTimings;
|
||||
|
||||
typedef struct {
|
||||
uint8_t hSyncWidth;
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint16_t vFrontPorch;
|
||||
uint8_t vBackPorch;
|
||||
uint32_t pixelClock_kHz;
|
||||
} HandheldTimings;
|
||||
|
||||
typedef struct {
|
||||
uint8_t min;
|
||||
uint8_t max;
|
||||
} MinMaxRefreshRate;
|
||||
|
||||
typedef struct {
|
||||
uint32_t unk0;
|
||||
uint32_t hActive;
|
||||
uint32_t vActive;
|
||||
uint32_t hSyncWidth;
|
||||
uint32_t vSyncWidth;
|
||||
uint32_t hFrontPorch;
|
||||
uint32_t vFrontPorch;
|
||||
uint32_t hBackPorch;
|
||||
uint32_t vBackPorch;
|
||||
uint32_t pclkKHz;
|
||||
uint32_t bitsPerPixel;
|
||||
uint32_t vmode;
|
||||
uint32_t sync;
|
||||
uint32_t unk1;
|
||||
uint32_t reserved;
|
||||
} NvdcMode2;
|
||||
|
||||
typedef struct {
|
||||
NvdcMode2 modes[201];
|
||||
uint32_t num_modes;
|
||||
} NvdcModeDB2;
|
||||
|
||||
typedef struct {
|
||||
unsigned int PLLD_DIVM: 8;
|
||||
unsigned int reserved_1: 3;
|
||||
unsigned int PLLD_DIVN: 8;
|
||||
unsigned int reserved_2: 1;
|
||||
unsigned int PLLD_DIVP: 3;
|
||||
unsigned int CSI_CLK_SRC: 1;
|
||||
unsigned int reserved_3: 1;
|
||||
unsigned int PLL_D: 1;
|
||||
unsigned int reserved_4: 1;
|
||||
unsigned int PLLD_LOCK: 1;
|
||||
unsigned int reserved_5: 1;
|
||||
unsigned int PLLD_REF_DIS: 1;
|
||||
unsigned int PLLD_ENABLE: 1;
|
||||
unsigned int PLLD_BYPASS: 1;
|
||||
} PLLD_BASE;
|
||||
|
||||
typedef struct {
|
||||
signed int PLLD_SDM_DIN: 16;
|
||||
unsigned int PLLD_EN_SDM: 1;
|
||||
unsigned int PLLD_LOCK_OVERRIDE: 1;
|
||||
unsigned int PLLD_EN_LCKDET: 1;
|
||||
unsigned int PLLD_FREQLOCK: 1;
|
||||
unsigned int PLLD_IDDQ: 1;
|
||||
unsigned int PLLD_ENABLE_CLK: 1;
|
||||
unsigned int PLLD_KVCO: 1;
|
||||
unsigned int PLLD_KCP: 2;
|
||||
unsigned int PLLD_PTS: 2;
|
||||
unsigned int PLLD_LDPULSE_ADJ: 3;
|
||||
unsigned int reserved: 2;
|
||||
} PLLD_MISC;
|
||||
|
||||
typedef struct {
|
||||
uint64_t clkVirtAddr;
|
||||
uint64_t dsiVirtAddr;
|
||||
bool isDocked;
|
||||
bool isLite;
|
||||
bool isRetroSUPER;
|
||||
bool isPossiblySpoofedRetro;
|
||||
bool dontForce60InDocked;
|
||||
bool matchLowestDocked;
|
||||
bool displaySync;
|
||||
bool displaySyncOutOfFocus60;
|
||||
bool displaySyncDocked;
|
||||
bool displaySyncDockedOutOfFocus60;
|
||||
} DisplayRefreshConfig;
|
||||
bool Initialize(const DisplayRefreshConfig* config);
|
||||
void SetDockedState(bool isDocked);
|
||||
bool SetRate(uint32_t new_refreshRate);
|
||||
bool GetRate(uint32_t* out_refreshRate, bool internal);
|
||||
uint8_t GetDockedHighestAllowed(void);
|
||||
void CorrectOledGamma(uint32_t refresh_rate);
|
||||
void SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p);
|
||||
void Shutdown(void);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,260 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <sysclk.h>
|
||||
#include <switch.h>
|
||||
#include "config.h"
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
#include "integrations.h"
|
||||
|
||||
class SysDockIntegration;
|
||||
class SaltyNXIntegration;
|
||||
class ClockManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Get instance
|
||||
* @return Pointer to a ClockManager instance
|
||||
*/
|
||||
static ClockManager* GetInstance();
|
||||
static void Initialize();
|
||||
static void Exit();
|
||||
ClockManager();
|
||||
virtual ~ClockManager();
|
||||
|
||||
/**
|
||||
* Get context object
|
||||
* @return Context instance
|
||||
*/
|
||||
SysClkContext GetCurrentContext();
|
||||
|
||||
/**
|
||||
* Get config object
|
||||
* @return Pointer to a config instance
|
||||
*/
|
||||
Config* GetConfig();
|
||||
|
||||
/**
|
||||
* Set clock manager running
|
||||
* @param running Is running or not?
|
||||
*/
|
||||
void SetRunning(bool running);
|
||||
|
||||
/**
|
||||
* Is clock manager running
|
||||
* @return running or not?
|
||||
*/
|
||||
bool Running();
|
||||
|
||||
/**
|
||||
* Get frequency list from clkrst
|
||||
*
|
||||
* @param module Module to get frequency list for
|
||||
* @param list List of frequencies
|
||||
* @param maxCount How many entries to expect in list. Usually 32
|
||||
* @param outCount How many entries were retrived
|
||||
*/
|
||||
void GetFreqList(SysClkModule module, std::uint32_t* list, std::uint32_t maxCount, std::uint32_t* outCount);
|
||||
|
||||
/**
|
||||
* Handles safety features
|
||||
*
|
||||
*/
|
||||
void HandleSafetyFeatures();
|
||||
|
||||
/**
|
||||
* Handles misc features (currently only battery charge current).
|
||||
*
|
||||
*/
|
||||
void HandleMiscFeatures();
|
||||
|
||||
/**
|
||||
* Handles governor state resolution and applies CPU/GPU governor transitions.
|
||||
*
|
||||
* @param targetHz Governor override value for the current profile.
|
||||
*/
|
||||
void HandleGovernor(uint32_t targetHz);
|
||||
|
||||
/**
|
||||
* Handles DVFS logic before the frequency set
|
||||
*
|
||||
* @param targetHz Governor override value for the current profile.
|
||||
*/
|
||||
void DVFSBeforeSet(u32 targetHz);
|
||||
|
||||
/**
|
||||
* Handles DVFS logic after the frequency set
|
||||
*
|
||||
* @param targetHz Governor override value for the current profile.
|
||||
*/
|
||||
void DVFSAfterSet(u32 targetHz);
|
||||
|
||||
/**
|
||||
* Reset the GPU vMin
|
||||
*
|
||||
*/
|
||||
void DVFSReset();
|
||||
|
||||
/**
|
||||
* Handles the Live CPU UV Feature
|
||||
*
|
||||
*/
|
||||
void HandleCpuUv();
|
||||
|
||||
/**
|
||||
* Handles frequency resets
|
||||
*
|
||||
* @param module The module to reset frequency for
|
||||
* @param isBoost Is in boost mode
|
||||
*/
|
||||
void HandleFreqReset(SysClkModule module, bool isBoost);
|
||||
|
||||
/**
|
||||
* Sets clocks
|
||||
*
|
||||
* @param isBoost Is in boost mode
|
||||
*/
|
||||
void SetClocks(bool isBoost);
|
||||
|
||||
/**
|
||||
* Main function, runs every 5s in sleep mode, and a user specified amount when awake
|
||||
*
|
||||
*/
|
||||
void Tick();
|
||||
|
||||
/**
|
||||
* Reset CPU/GPU to stock values
|
||||
*
|
||||
*/
|
||||
void ResetToStockClocks();
|
||||
|
||||
/**
|
||||
* Wait for the next tick event
|
||||
*
|
||||
*/
|
||||
void WaitForNextTick();
|
||||
|
||||
/**
|
||||
* Set the data in the KIP
|
||||
*
|
||||
*/
|
||||
void SetKipData();
|
||||
|
||||
/**
|
||||
* Get the data from the KIP
|
||||
*
|
||||
*/
|
||||
void GetKipData();
|
||||
|
||||
/**
|
||||
* Runs the CPU Governor
|
||||
*
|
||||
* @param arg Cast to ClockManager* for context
|
||||
*/
|
||||
static void CpuGovernorThread(void* arg);
|
||||
|
||||
/**
|
||||
* Runs the GPU Governor
|
||||
*
|
||||
* @param arg Cast to ClockManager* for context
|
||||
*/
|
||||
static void GovernorThread(void* arg);
|
||||
|
||||
/**
|
||||
* Runs the VRR Algorithm
|
||||
*
|
||||
* @param arg Cast to ClockManager* for context
|
||||
*/
|
||||
static void VRRThread(void* arg);
|
||||
|
||||
/**
|
||||
* Frequency table
|
||||
*
|
||||
*/
|
||||
struct FreqTable {
|
||||
std::uint32_t count;
|
||||
std::uint32_t list[SYSCLK_FREQ_LIST_MAX];
|
||||
} freqTable[SysClkModule_EnumMax];
|
||||
|
||||
/**
|
||||
* Gets the current GPU speedo bracket
|
||||
*
|
||||
* @param speedo GPU Speedo
|
||||
*/
|
||||
int GetSpeedoBracket (int speedo);
|
||||
|
||||
/**
|
||||
* Gets the required vMin for a ram frequency for a speedo
|
||||
*
|
||||
* @param freq RAM Freq in MHz
|
||||
* @param speedo GPU 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:
|
||||
bool IsAssignableHz(SysClkModule module, std::uint32_t hz);
|
||||
inline std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile);
|
||||
std::uint32_t GetNearestHz(SysClkModule module, std::uint32_t inHz, std::uint32_t maxHz);
|
||||
bool ConfigIntervalTimeout(SysClkConfigValue intervalMsConfigValue, std::uint64_t ns, std::uint64_t* lastLogNs);
|
||||
void RefreshFreqTableRow(SysClkModule module);
|
||||
bool RefreshContext();
|
||||
static ClockManager *instance;
|
||||
std::atomic_bool running;
|
||||
LockableMutex contextMutex;
|
||||
Config* config;
|
||||
SysClkContext* context;
|
||||
std::uint64_t lastTempLogNs;
|
||||
std::uint64_t lastFreqLogNs;
|
||||
std::uint64_t lastPowerLogNs;
|
||||
std::uint64_t lastCsvWriteNs;
|
||||
SysDockIntegration *sysDockIntegration;
|
||||
SaltyNXIntegration *saltyNXIntegration;
|
||||
};
|
||||
69
Source/rewrite-hoc-clk/sysmodule/src/clock_manager.hpp
Normal file
69
Source/rewrite-hoc-clk/sysmodule/src/clock_manager.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sysclk.h>
|
||||
#include <switch.h>
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
|
||||
namespace clockManager {
|
||||
|
||||
struct FreqTable {
|
||||
std::uint32_t count;
|
||||
std::uint32_t list[SYSCLK_FREQ_LIST_MAX];
|
||||
};
|
||||
|
||||
|
||||
extern bool hasChanged;
|
||||
|
||||
// instance variables
|
||||
extern bool gRunning;
|
||||
extern LockableMutex gContextMutex;
|
||||
extern SysClkContext gContext;
|
||||
extern FreqTable gFreqTable[SysClkModule_EnumMax];
|
||||
extern std::uint64_t gLastTempLogNs;
|
||||
extern std::uint64_t gLastFreqLogNs;
|
||||
extern std::uint64_t gLastPowerLogNs;
|
||||
extern std::uint64_t gLastCsvWriteNs;
|
||||
|
||||
|
||||
void Initialize();
|
||||
void Exit();
|
||||
|
||||
SysClkContext GetCurrentContext();
|
||||
|
||||
void SetRunning(bool running);
|
||||
bool Running();
|
||||
|
||||
std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile);
|
||||
bool IsAssignableHz(SysClkModule module, std::uint32_t hz);
|
||||
|
||||
void GetFreqList(SysClkModule module, std::uint32_t* list, std::uint32_t maxCount, std::uint32_t* outCount);
|
||||
|
||||
void Tick();
|
||||
void WaitForNextTick();
|
||||
}
|
||||
@@ -24,497 +24,454 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include "config.hpp"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <initializer_list>
|
||||
#include <minIni.h>
|
||||
#include <nxExt.h>
|
||||
#include "board/board.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "file_utils.hpp"
|
||||
|
||||
Config::Config(std::string path)
|
||||
{
|
||||
this->path = path;
|
||||
this->loaded = false;
|
||||
this->profileMHzMap = std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t>();
|
||||
this->profileCountMap = std::map<std::uint64_t, std::uint8_t>();
|
||||
this->mtime = 0;
|
||||
this->enabled = false;
|
||||
for(unsigned int i = 0; i < SysClkModule_EnumMax; i++)
|
||||
{
|
||||
this->overrideFreqs[i] = 0;
|
||||
}
|
||||
namespace config {
|
||||
|
||||
for(unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++)
|
||||
{
|
||||
this->configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
uint64_t configValues[SysClkConfigValue_EnumMax];
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
this->Close();
|
||||
}
|
||||
namespace {
|
||||
|
||||
Config* Config::CreateDefault()
|
||||
{
|
||||
return new Config(FILE_CONFIG_DIR "/config.ini");
|
||||
}
|
||||
bool gLoaded = false;
|
||||
std::string gPath;
|
||||
time_t gMtime = 0;
|
||||
std::atomic_bool gEnabled{false};
|
||||
std::uint32_t gOverrideFreqs[SysClkModule_EnumMax];
|
||||
std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t> gProfileMHzMap;
|
||||
std::map<std::uint64_t, std::uint8_t> gProfileCountMap;
|
||||
LockableMutex gConfigMutex;
|
||||
LockableMutex gOverrideMutex;
|
||||
|
||||
void Config::Load()
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Reading %s", this->path.c_str());
|
||||
|
||||
this->Close();
|
||||
this->mtime = this->CheckModificationTime();
|
||||
if(!this->mtime)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Error finding file");
|
||||
}
|
||||
else if (!ini_browse(&BrowseIniFunc, this, this->path.c_str()))
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Error loading file");
|
||||
}
|
||||
|
||||
this->loaded = true;
|
||||
}
|
||||
|
||||
void Config::Close()
|
||||
{
|
||||
this->loaded = false;
|
||||
this->profileMHzMap.clear();
|
||||
this->profileCountMap.clear();
|
||||
|
||||
for(unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++)
|
||||
{
|
||||
this->configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::Refresh()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
if (!this->loaded || this->mtime != this->CheckModificationTime())
|
||||
{
|
||||
this->Load();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Config::HasProfilesLoaded()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
return this->loaded;
|
||||
}
|
||||
|
||||
time_t Config::CheckModificationTime()
|
||||
{
|
||||
time_t mtime = 0;
|
||||
struct stat st;
|
||||
if (stat(this->path.c_str(), &st) == 0)
|
||||
{
|
||||
mtime = st.st_mtime;
|
||||
}
|
||||
|
||||
return mtime;
|
||||
}
|
||||
|
||||
std::uint32_t Config::FindClockMHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile)
|
||||
{
|
||||
if (this->loaded)
|
||||
{
|
||||
std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t>::const_iterator it = this->profileMHzMap.find(std::make_tuple(tid, profile, module));
|
||||
if (it != this->profileMHzMap.end())
|
||||
{
|
||||
return it->second;
|
||||
time_t CheckModificationTime() {
|
||||
time_t mtime = 0;
|
||||
struct stat st;
|
||||
if (stat(gPath.c_str(), &st) == 0) {
|
||||
mtime = st.st_mtime;
|
||||
}
|
||||
return mtime;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
std::uint32_t FindClockMHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile) {
|
||||
if (gLoaded) {
|
||||
auto it = gProfileMHzMap.find(std::make_tuple(tid, profile, module));
|
||||
if (it != gProfileMHzMap.end()) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t Config::FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles, u32 mhzMultiplier)
|
||||
{
|
||||
std::uint32_t mhz = 0;
|
||||
std::uint32_t FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles, u32 mhzMultiplier = 1000000) {
|
||||
std::uint32_t mhz = 0;
|
||||
|
||||
if (this->loaded)
|
||||
{
|
||||
for(auto profile: profiles)
|
||||
{
|
||||
mhz = FindClockMHz(tid, module, profile);
|
||||
if (gLoaded) {
|
||||
for (auto profile: profiles) {
|
||||
mhz = FindClockMHz(tid, module, profile);
|
||||
if (mhz) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mhz)
|
||||
{
|
||||
break;
|
||||
return std::max((std::uint32_t)0, mhz * mhzMultiplier);
|
||||
}
|
||||
|
||||
int BrowseIniFunc(const char* section, const char* key, const char* value, void* userdata) {
|
||||
(void)userdata;
|
||||
std::uint64_t input;
|
||||
if (!strcmp(section, CONFIG_VAL_SECTION)) {
|
||||
for (unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++) {
|
||||
if (!strcmp(key, sysclkFormatConfigValue((SysClkConfigValue)kval, false))) {
|
||||
input = strtoul(value, NULL, 0);
|
||||
if (!sysclkValidConfigValue((SysClkConfigValue)kval, input)) {
|
||||
input = sysclkDefaultConfigValue((SysClkConfigValue)kval);
|
||||
fileUtils::LogLine("[cfg] Invalid value for key '%s' in section '%s': using default %d", key, section, input);
|
||||
}
|
||||
configValues[kval] = input;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized config value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint64_t tid = strtoul(section, NULL, 16);
|
||||
|
||||
if (!tid || strlen(section) != 16) {
|
||||
fileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid TitleID", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SysClkProfile parsedProfile = SysClkProfile_EnumMax;
|
||||
SysClkModule parsedModule = SysClkModule_EnumMax;
|
||||
|
||||
for (unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++) {
|
||||
const char* profileCode = board::GetProfileName((SysClkProfile)profile, false);
|
||||
size_t profileCodeLen = strlen(profileCode);
|
||||
|
||||
if (!strncmp(key, profileCode, profileCodeLen) && key[profileCodeLen] == '_') {
|
||||
const char* subkey = key + profileCodeLen + 1;
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) {
|
||||
const char* moduleCode = board::GetModuleName((SysClkModule)module, false);
|
||||
size_t moduleCodeLen = strlen(moduleCode);
|
||||
if (!strncmp(subkey, moduleCode, moduleCodeLen) && subkey[moduleCodeLen] == '\0') {
|
||||
parsedProfile = (SysClkProfile)profile;
|
||||
parsedModule = (SysClkModule)module;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedModule == SysClkModule_EnumMax || parsedProfile == SysClkProfile_EnumMax) {
|
||||
fileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized key", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint32_t mhz = strtoul(value, NULL, 10);
|
||||
if (!mhz) {
|
||||
fileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
gProfileMHzMap[std::make_tuple(tid, parsedProfile, parsedModule)] = mhz;
|
||||
auto it = gProfileCountMap.find(tid);
|
||||
if (it == gProfileCountMap.end()) {
|
||||
gProfileCountMap[tid] = 1;
|
||||
} else {
|
||||
it->second++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Close() {
|
||||
gLoaded = false;
|
||||
gProfileMHzMap.clear();
|
||||
gProfileCountMap.clear();
|
||||
for (unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++) {
|
||||
configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::max((std::uint32_t)0, mhz * mhzMultiplier);
|
||||
}
|
||||
void Load() {
|
||||
fileUtils::LogLine("[cfg] Reading %s", gPath.c_str());
|
||||
|
||||
std::uint32_t Config::GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile, bool returnRaw)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
switch(profile)
|
||||
{
|
||||
case SysClkProfile_Handheld:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case SysClkProfile_HandheldCharging:
|
||||
case SysClkProfile_HandheldChargingUSB:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingUSB, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case SysClkProfile_HandheldChargingOfficial:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingOfficial, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case SysClkProfile_Docked:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Docked}, returnRaw ? 1 : 1000000);
|
||||
default:
|
||||
ERROR_THROW("Unhandled SysClkProfile: %u", profile);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Config::GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
out_profiles->mhzMap[profile][module] = FindClockMHz(tid, (SysClkModule)module, (SysClkProfile)profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
uint8_t numProfiles = 0;
|
||||
|
||||
char section[17] = {0};
|
||||
snprintf(section, sizeof(section), "%016lX", tid);
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
keys.reserve(SysClkProfile_EnumMax * SysClkModule_EnumMax);
|
||||
values.reserve(SysClkProfile_EnumMax * SysClkModule_EnumMax);
|
||||
|
||||
std::uint32_t* mhz = &profiles->mhz[0];
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
if(*mhz)
|
||||
{
|
||||
numProfiles++;
|
||||
|
||||
std::string key = std::string(board::GetProfileName((SysClkProfile)profile, false)) +
|
||||
"_" +
|
||||
board::GetModuleName((SysClkModule)module, false);
|
||||
std::string value = std::to_string(*mhz);
|
||||
|
||||
keys.push_back(key);
|
||||
values.push_back(value);
|
||||
Close();
|
||||
gMtime = CheckModificationTime();
|
||||
if (!gMtime) {
|
||||
fileUtils::LogLine("[cfg] Error finding file");
|
||||
} else if (!ini_browse(&BrowseIniFunc, nullptr, gPath.c_str())) {
|
||||
fileUtils::LogLine("[cfg] Error loading file");
|
||||
}
|
||||
mhz++;
|
||||
|
||||
gLoaded = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
gPath = FILE_CONFIG_DIR "/config.ini";
|
||||
gLoaded = false;
|
||||
gProfileMHzMap.clear();
|
||||
gProfileCountMap.clear();
|
||||
gMtime = 0;
|
||||
gEnabled = false;
|
||||
for (unsigned int i = 0; i < SysClkModule_EnumMax; i++) {
|
||||
gOverrideFreqs[i] = 0;
|
||||
}
|
||||
for (unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++) {
|
||||
configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const char*> keyPointers;
|
||||
std::vector<const char*> valuePointers;
|
||||
keyPointers.reserve(keys.size() + 1);
|
||||
valuePointers.reserve(values.size() + 1);
|
||||
|
||||
for(size_t i = 0; i < keys.size(); i++) {
|
||||
keyPointers.push_back(keys[i].c_str());
|
||||
valuePointers.push_back(values[i].c_str());
|
||||
void Exit() {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
Close();
|
||||
}
|
||||
keyPointers.push_back(NULL);
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if(!ini_putsection(section, keyPointers.data(), valuePointers.data(), this->path.c_str()))
|
||||
{
|
||||
bool Refresh() {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
if (!gLoaded || gMtime != CheckModificationTime()) {
|
||||
Load();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(immediate)
|
||||
{
|
||||
mhz = &profiles->mhz[0];
|
||||
this->profileCountMap[tid] = numProfiles;
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
if(*mhz)
|
||||
{
|
||||
this->profileMHzMap[std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module)] = *mhz;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->profileMHzMap.erase(std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module));
|
||||
bool HasProfilesLoaded() {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
return gLoaded;
|
||||
}
|
||||
|
||||
std::uint32_t GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile, bool returnRaw) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
switch (profile) {
|
||||
case SysClkProfile_Handheld:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case SysClkProfile_HandheldCharging:
|
||||
case SysClkProfile_HandheldChargingUSB:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingUSB, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case SysClkProfile_HandheldChargingOfficial:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingOfficial, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case SysClkProfile_Docked:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Docked}, returnRaw ? 1 : 1000000);
|
||||
default:
|
||||
ERROR_THROW("Unhandled SysClkProfile: %u", profile);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
for (unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++) {
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) {
|
||||
out_profiles->mhzMap[profile][module] = FindClockMHz(tid, (SysClkModule)module, (SysClkProfile)profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
uint8_t numProfiles = 0;
|
||||
|
||||
char section[17] = {0};
|
||||
snprintf(section, sizeof(section), "%016lX", tid);
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
keys.reserve(SysClkProfile_EnumMax * SysClkModule_EnumMax);
|
||||
values.reserve(SysClkProfile_EnumMax * SysClkModule_EnumMax);
|
||||
|
||||
std::uint32_t* mhz = &profiles->mhz[0];
|
||||
|
||||
for (unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++) {
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) {
|
||||
if (*mhz) {
|
||||
numProfiles++;
|
||||
|
||||
std::string key = std::string(board::GetProfileName((SysClkProfile)profile, false)) +
|
||||
"_" +
|
||||
board::GetModuleName((SysClkModule)module, false);
|
||||
std::string value = std::to_string(*mhz);
|
||||
|
||||
keys.push_back(key);
|
||||
values.push_back(value);
|
||||
}
|
||||
mhz++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
std::vector<const char*> keyPointers;
|
||||
std::vector<const char*> valuePointers;
|
||||
keyPointers.reserve(keys.size() + 1);
|
||||
valuePointers.reserve(values.size() + 1);
|
||||
|
||||
std::uint8_t Config::GetProfileCount(std::uint64_t tid)
|
||||
{
|
||||
std::map<std::uint64_t, std::uint8_t>::iterator it = this->profileCountMap.find(tid);
|
||||
if (it == this->profileCountMap.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
keyPointers.push_back(keys[i].c_str());
|
||||
valuePointers.push_back(values[i].c_str());
|
||||
}
|
||||
keyPointers.push_back(NULL);
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int Config::BrowseIniFunc(const char* section, const char* key, const char* value, void* userdata)
|
||||
{
|
||||
Config* config = (Config*)userdata;
|
||||
std::uint64_t input;
|
||||
if(!strcmp(section, CONFIG_VAL_SECTION))
|
||||
{
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(!strcmp(key, sysclkFormatConfigValue((SysClkConfigValue)kval, false)))
|
||||
{
|
||||
input = strtoul(value, NULL, 0);
|
||||
if(!sysclkValidConfigValue((SysClkConfigValue)kval, input))
|
||||
{
|
||||
input = sysclkDefaultConfigValue((SysClkConfigValue)kval);
|
||||
FileUtils::LogLine("[cfg] Invalid value for key '%s' in section '%s': using default %d", key, section, input);
|
||||
}
|
||||
config->configValues[kval] = input;
|
||||
return 1;
|
||||
}
|
||||
if (!ini_putsection(section, keyPointers.data(), valuePointers.data(), gPath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized config value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint64_t tid = strtoul(section, NULL, 16);
|
||||
|
||||
if(!tid || strlen(section) != 16)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid TitleID", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SysClkProfile parsedProfile = SysClkProfile_EnumMax;
|
||||
SysClkModule parsedModule = SysClkModule_EnumMax;
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
const char* profileCode = board::GetProfileName((SysClkProfile)profile, false);
|
||||
size_t profileCodeLen = strlen(profileCode);
|
||||
|
||||
if(!strncmp(key, profileCode, profileCodeLen) && key[profileCodeLen] == '_')
|
||||
{
|
||||
const char* subkey = key + profileCodeLen + 1;
|
||||
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
const char* moduleCode = board::GetModuleName((SysClkModule)module, false);
|
||||
size_t moduleCodeLen = strlen(moduleCode);
|
||||
if(!strncmp(subkey, moduleCode, moduleCodeLen) && subkey[moduleCodeLen] == '\0')
|
||||
{
|
||||
parsedProfile = (SysClkProfile)profile;
|
||||
parsedModule = (SysClkModule)module;
|
||||
if (immediate) {
|
||||
mhz = &profiles->mhz[0];
|
||||
gProfileCountMap[tid] = numProfiles;
|
||||
for (unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++) {
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) {
|
||||
if (*mhz) {
|
||||
gProfileMHzMap[std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module)] = *mhz;
|
||||
} else {
|
||||
gProfileMHzMap.erase(std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module));
|
||||
}
|
||||
mhz++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(parsedModule == SysClkModule_EnumMax || parsedProfile == SysClkProfile_EnumMax)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized key", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint32_t mhz = strtoul(value, NULL, 10);
|
||||
if(!mhz)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
config->profileMHzMap[std::make_tuple(tid, parsedProfile, parsedModule)] = mhz;
|
||||
std::map<std::uint64_t, std::uint8_t>::iterator it = config->profileCountMap.find(tid);
|
||||
if (it == config->profileCountMap.end())
|
||||
{
|
||||
config->profileCountMap[tid] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
it->second++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Config::SetEnabled(bool enabled)
|
||||
{
|
||||
this->enabled = enabled;
|
||||
}
|
||||
|
||||
bool Config::Enabled()
|
||||
{
|
||||
return this->enabled;
|
||||
}
|
||||
|
||||
void Config::SetOverrideHz(SysClkModule module, std::uint32_t hz)
|
||||
{
|
||||
ASSERT_ENUM_VALID(SysClkModule, module);
|
||||
|
||||
std::scoped_lock lock{this->overrideMutex};
|
||||
|
||||
this->overrideFreqs[module] = hz;
|
||||
}
|
||||
|
||||
std::uint32_t Config::GetOverrideHz(SysClkModule module)
|
||||
{
|
||||
ASSERT_ENUM_VALID(SysClkModule, module);
|
||||
|
||||
std::scoped_lock lock{this->overrideMutex};
|
||||
|
||||
return this->overrideFreqs[module];
|
||||
}
|
||||
|
||||
std::uint64_t Config::GetConfigValue(SysClkConfigValue kval)
|
||||
{
|
||||
ASSERT_ENUM_VALID(SysClkConfigValue, kval);
|
||||
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
return this->configValues[kval];
|
||||
}
|
||||
|
||||
const char* Config::GetConfigValueName(SysClkConfigValue kval, bool pretty)
|
||||
{
|
||||
ASSERT_ENUM_VALID(SysClkConfigValue, kval);
|
||||
|
||||
const char* result = sysclkFormatConfigValue(kval, pretty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Config::GetConfigValues(SysClkConfigValueList* out_configValues)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
out_configValues->values[kval] = this->configValues[kval];
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::SetConfigValues(SysClkConfigValueList* configValues, bool immediate)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
std::vector<const char*> iniKeys;
|
||||
std::vector<std::string> iniValues;
|
||||
|
||||
iniKeys.reserve(SysClkConfigValue_EnumMax + 1);
|
||||
iniValues.reserve(SysClkConfigValue_EnumMax);
|
||||
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(!sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval]) ||
|
||||
configValues->values[kval] == sysclkDefaultConfigValue((SysClkConfigValue)kval))
|
||||
{
|
||||
continue;
|
||||
std::uint8_t GetProfileCount(std::uint64_t tid) {
|
||||
auto it = gProfileCountMap.find(tid);
|
||||
if (it == gProfileCountMap.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
iniValues.push_back(std::to_string(configValues->values[kval]));
|
||||
iniKeys.push_back(sysclkFormatConfigValue((SysClkConfigValue)kval, false));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Null terminate
|
||||
iniKeys.push_back(NULL);
|
||||
|
||||
// Build pointer array for ini function
|
||||
std::vector<const char*> valuePointers;
|
||||
valuePointers.reserve(iniValues.size() + 1);
|
||||
for(const auto& val : iniValues) {
|
||||
valuePointers.push_back(val.c_str());
|
||||
}
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if(!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), this->path.c_str()))
|
||||
{
|
||||
return false;
|
||||
void SetEnabled(bool enabled) {
|
||||
gEnabled = enabled;
|
||||
}
|
||||
|
||||
// Only actually apply changes in memory after a successful save
|
||||
if(immediate)
|
||||
{
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval]))
|
||||
{
|
||||
this->configValues[kval] = configValues->values[kval];
|
||||
}
|
||||
else
|
||||
{
|
||||
this->configValues[kval] = sysclkDefaultConfigValue((SysClkConfigValue)kval);
|
||||
}
|
||||
bool Enabled() {
|
||||
return gEnabled;
|
||||
}
|
||||
|
||||
void SetOverrideHz(SysClkModule module, std::uint32_t hz) {
|
||||
ASSERT_ENUM_VALID(SysClkModule, module);
|
||||
std::scoped_lock lock{gOverrideMutex};
|
||||
gOverrideFreqs[module] = hz;
|
||||
}
|
||||
|
||||
std::uint32_t GetOverrideHz(SysClkModule module) {
|
||||
ASSERT_ENUM_VALID(SysClkModule, module);
|
||||
std::scoped_lock lock{gOverrideMutex};
|
||||
return gOverrideFreqs[module];
|
||||
}
|
||||
|
||||
std::uint64_t GetConfigValue(SysClkConfigValue kval) {
|
||||
ASSERT_ENUM_VALID(SysClkConfigValue, kval);
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
return configValues[kval];
|
||||
}
|
||||
|
||||
const char* GetConfigValueName(SysClkConfigValue kval, bool pretty) {
|
||||
ASSERT_ENUM_VALID(SysClkConfigValue, kval);
|
||||
return sysclkFormatConfigValue(kval, pretty);
|
||||
}
|
||||
|
||||
void GetConfigValues(SysClkConfigValueList* out_configValues) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
for (unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++) {
|
||||
out_configValues->values[kval] = configValues[kval];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
bool SetConfigValues(SysClkConfigValueList* configValues, bool immediate) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
|
||||
std::vector<const char*> iniKeys;
|
||||
std::vector<std::string> iniValues;
|
||||
iniKeys.reserve(SysClkConfigValue_EnumMax + 1);
|
||||
iniValues.reserve(SysClkConfigValue_EnumMax);
|
||||
|
||||
for (unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++) {
|
||||
if (!sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval]) ||
|
||||
configValues->values[kval] == sysclkDefaultConfigValue((SysClkConfigValue)kval)) {
|
||||
continue;
|
||||
}
|
||||
iniValues.push_back(std::to_string(configValues->values[kval]));
|
||||
iniKeys.push_back(sysclkFormatConfigValue((SysClkConfigValue)kval, false));
|
||||
}
|
||||
|
||||
iniKeys.push_back(NULL);
|
||||
|
||||
std::vector<const char*> valuePointers;
|
||||
valuePointers.reserve(iniValues.size() + 1);
|
||||
for (const auto& val : iniValues) {
|
||||
valuePointers.push_back(val.c_str());
|
||||
}
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if (!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), gPath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
for (unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++) {
|
||||
if (sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval])) {
|
||||
config::configValues[kval] = configValues->values[kval];
|
||||
} else {
|
||||
config::configValues[kval] = sysclkDefaultConfigValue((SysClkConfigValue)kval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResetConfigValue(SysClkConfigValue kval) {
|
||||
if (!SYSCLK_ENUM_VALID(SysClkConfigValue, kval)) {
|
||||
fileUtils::LogLine("[cfg] Invalid SysClkConfigValue: %u", kval);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
|
||||
std::uint64_t defaultValue = sysclkDefaultConfigValue(kval);
|
||||
|
||||
std::vector<const char*> iniKeys;
|
||||
std::vector<std::string> iniValues;
|
||||
iniKeys.reserve(2);
|
||||
iniValues.reserve(1);
|
||||
|
||||
iniKeys.push_back(sysclkFormatConfigValue(kval, false));
|
||||
iniValues.push_back("");
|
||||
iniKeys.push_back(NULL);
|
||||
|
||||
std::vector<const char*> valuePointers;
|
||||
valuePointers.reserve(iniValues.size() + 1);
|
||||
for (const auto& val : iniValues) {
|
||||
valuePointers.push_back(val.c_str());
|
||||
}
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if (!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), gPath.c_str())) {
|
||||
fileUtils::LogLine("[cfg] Failed to reset config value %u in INI", kval);
|
||||
return false;
|
||||
}
|
||||
|
||||
configValues[kval] = defaultValue;
|
||||
fileUtils::LogLine("[cfg] Reset config value %u to default: %llu", kval, defaultValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetConfigValue(SysClkConfigValue kval, std::uint64_t value, bool immediate) {
|
||||
if (!SYSCLK_ENUM_VALID(SysClkConfigValue, kval)) {
|
||||
return false;
|
||||
}
|
||||
if (!sysclkValidConfigValue(kval, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
|
||||
std::vector<const char*> iniKeys;
|
||||
std::vector<std::string> iniValues;
|
||||
iniKeys.reserve(2);
|
||||
iniValues.reserve(1);
|
||||
|
||||
iniKeys.push_back(sysclkFormatConfigValue(kval, false));
|
||||
iniValues.push_back(std::to_string(value));
|
||||
iniKeys.push_back(NULL);
|
||||
|
||||
std::vector<const char*> valuePointers;
|
||||
valuePointers.reserve(2);
|
||||
valuePointers.push_back(iniValues[0].c_str());
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if (!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), gPath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
configValues[kval] = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Config::ResetConfigValue(SysClkConfigValue kval)
|
||||
{
|
||||
if (!SYSCLK_ENUM_VALID(SysClkConfigValue, kval)) {
|
||||
FileUtils::LogLine("[cfg] Invalid SysClkConfigValue: %u", kval);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
std::uint64_t defaultValue = sysclkDefaultConfigValue(kval);
|
||||
|
||||
std::vector<const char*> iniKeys;
|
||||
std::vector<std::string> iniValues;
|
||||
|
||||
iniKeys.reserve(2);
|
||||
iniValues.reserve(1);
|
||||
|
||||
const char* keyStr = sysclkFormatConfigValue(kval, false);
|
||||
|
||||
iniKeys.push_back(keyStr);
|
||||
iniValues.push_back("");
|
||||
|
||||
iniKeys.push_back(NULL);
|
||||
|
||||
std::vector<const char*> valuePointers;
|
||||
valuePointers.reserve(iniValues.size() + 1);
|
||||
for (const auto& val : iniValues) {
|
||||
valuePointers.push_back(val.c_str());
|
||||
}
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if (!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), this->path.c_str())) {
|
||||
FileUtils::LogLine("[cfg] Failed to reset config value %u in INI", kval);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->configValues[kval] = defaultValue;
|
||||
FileUtils::LogLine("[cfg] Reset config value %u to default: %llu", kval, defaultValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -24,31 +24,19 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
|
||||
#include <sysclk.h>
|
||||
#include <switch.h>
|
||||
#include <minIni.h>
|
||||
#include <nxExt.h>
|
||||
#include "board/board.hpp"
|
||||
|
||||
#define CONFIG_VAL_SECTION "values"
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
Config(std::string path);
|
||||
virtual ~Config();
|
||||
namespace config {
|
||||
|
||||
static Config* CreateDefault();
|
||||
void Initialize();
|
||||
void Exit();
|
||||
|
||||
bool Refresh();
|
||||
|
||||
bool HasProfilesLoaded();
|
||||
|
||||
std::uint8_t GetProfileCount(std::uint64_t tid);
|
||||
@@ -66,26 +54,8 @@ class Config
|
||||
void GetConfigValues(SysClkConfigValueList* out_configValues);
|
||||
bool SetConfigValues(SysClkConfigValueList* configValues, bool immediate);
|
||||
bool ResetConfigValue(SysClkConfigValue kval);
|
||||
bool SetInternalValues(bool immediate);
|
||||
bool SetConfigValue(SysClkConfigValue kval, std::uint64_t value, bool immediate = true);
|
||||
|
||||
uint64_t configValues[SysClkConfigValue_EnumMax];
|
||||
protected:
|
||||
void Load();
|
||||
void Close();
|
||||
bool kipOverride[SysClkConfigValue_EnumMax];
|
||||
time_t CheckModificationTime();
|
||||
std::uint32_t FindClockMHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile);
|
||||
std::uint32_t FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles, u32 mhzMultiplier = 1000000);
|
||||
static int BrowseIniFunc(const char* section, const char* key, const char* value, void* userdata);
|
||||
extern uint64_t configValues[SysClkConfigValue_EnumMax];
|
||||
|
||||
std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t> profileMHzMap;
|
||||
std::map<std::uint64_t, std::uint8_t> profileCountMap;
|
||||
bool loaded;
|
||||
std::string path;
|
||||
time_t mtime;
|
||||
LockableMutex configMutex;
|
||||
LockableMutex overrideMutex;
|
||||
std::atomic_bool enabled;
|
||||
std::uint32_t overrideFreqs[SysClkModule_EnumMax];
|
||||
};
|
||||
}
|
||||
@@ -12,9 +12,9 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
@@ -24,31 +24,34 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "errors.h"
|
||||
#include "errors.hpp"
|
||||
#include "file_utils.hpp"
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include "file_utils.h"
|
||||
void Errors::ThrowException(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const char* msg = Errors::FormatMessage(format, args);
|
||||
va_end(args);
|
||||
FileUtils::LogLine(format, args);
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
const char* Errors::FormatMessage(const char* format, va_list args)
|
||||
{
|
||||
size_t len = vsnprintf(NULL, 0, format, args) * sizeof(char);
|
||||
char* buf = (char*)malloc(len + 1);
|
||||
if (buf == NULL)
|
||||
{
|
||||
return format;
|
||||
namespace errors {
|
||||
|
||||
namespace {
|
||||
|
||||
const char* FormatMessage(const char* format, va_list args) {
|
||||
size_t len = vsnprintf(NULL, 0, format, args) * sizeof(char);
|
||||
char* buf = (char*)malloc(len + 1);
|
||||
if (buf == NULL) {
|
||||
return format;
|
||||
}
|
||||
vsnprintf(buf, len + 1, format, args);
|
||||
return buf;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vsnprintf(buf, len + 1, format, args);
|
||||
void ThrowException(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const char* msg = FormatMessage(format, args);
|
||||
va_end(args);
|
||||
fileUtils::LogLine(format, args);
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
@@ -24,29 +24,25 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#define ERROR_THROW(format, ...) Errors::ThrowException(format "\n in %s:%u", ##__VA_ARGS__, __FILE__, __LINE__)
|
||||
#define ERROR_THROW(format, ...) errors::ThrowException(format "\n in %s:%u", ##__VA_ARGS__, __FILE__, __LINE__)
|
||||
#define ERROR_RESULT_THROW(rc, format, ...) ERROR_THROW(format "\n RC: [0x%x] %04d-%04d", ##__VA_ARGS__, rc, R_MODULE(rc), R_DESCRIPTION(rc))
|
||||
#define ASSERT_RESULT_OK(rc, format, ...) \
|
||||
if (R_FAILED(rc)) \
|
||||
{ \
|
||||
#define ASSERT_RESULT_OK(rc, format, ...) \
|
||||
if (R_FAILED(rc)) \
|
||||
{ \
|
||||
ERROR_RESULT_THROW(rc, "ASSERT_RESULT_OK: " format, ##__VA_ARGS__); \
|
||||
}
|
||||
#define ASSERT_ENUM_VALID(n, v) \
|
||||
if(!SYSCLK_ENUM_VALID(n, v)) { \
|
||||
#define ASSERT_ENUM_VALID(n, v) \
|
||||
if (!SYSCLK_ENUM_VALID(n, v)) { \
|
||||
ERROR_THROW("No such %s: %u", #n, v); \
|
||||
}
|
||||
|
||||
class Errors
|
||||
{
|
||||
public:
|
||||
static void ThrowException(const char* format, ...);
|
||||
namespace errors {
|
||||
|
||||
protected:
|
||||
static const char* FormatMessage(const char* format, va_list args);
|
||||
};
|
||||
void ThrowException(const char* format, ...);
|
||||
|
||||
}
|
||||
@@ -12,9 +12,9 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
@@ -24,194 +24,177 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "file_utils.h"
|
||||
#include "file_utils.hpp"
|
||||
#include <nxExt.h>
|
||||
|
||||
static LockableMutex g_log_mutex;
|
||||
static LockableMutex g_csv_mutex;
|
||||
static std::atomic_bool g_has_initialized = false;
|
||||
static bool g_log_enabled = false;
|
||||
static std::uint64_t g_last_flag_check = 0;
|
||||
|
||||
extern "C" void __libnx_init_time(void);
|
||||
|
||||
static void _FileUtils_InitializeThreadFunc(void* args)
|
||||
{
|
||||
FileUtils::Initialize();
|
||||
}
|
||||
namespace fileUtils {
|
||||
|
||||
bool FileUtils::IsInitialized()
|
||||
{
|
||||
return g_has_initialized;
|
||||
}
|
||||
namespace {
|
||||
|
||||
void FileUtils::LogLine(const char* format, ...)
|
||||
{
|
||||
std::scoped_lock lock{g_log_mutex};
|
||||
LockableMutex g_log_mutex;
|
||||
LockableMutex g_csv_mutex;
|
||||
std::atomic_bool g_has_initialized = false;
|
||||
bool g_log_enabled = false;
|
||||
std::uint64_t g_last_flag_check = 0;
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (g_has_initialized)
|
||||
{
|
||||
FileUtils::RefreshFlags(false);
|
||||
void RefreshFlags(bool force) {
|
||||
std::uint64_t now = armTicksToNs(armGetSystemTick());
|
||||
if (!force && (now - g_last_flag_check) < FILE_FLAG_CHECK_INTERVAL_NS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(g_log_enabled)
|
||||
{
|
||||
FILE* file = fopen(FILE_LOG_FILE_PATH, "a");
|
||||
|
||||
if (file)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
struct tm* nowTm = localtime(&now.tv_sec);
|
||||
|
||||
fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d.%03ld] ", nowTm->tm_year+1900, nowTm->tm_mon+1, nowTm->tm_mday, nowTm->tm_hour, nowTm->tm_min, nowTm->tm_sec, now.tv_nsec / 1000000UL);
|
||||
vfprintf(file, format, args);
|
||||
fprintf(file, "\n");
|
||||
FILE* file = fopen(FILE_LOG_FLAG_PATH, "r");
|
||||
if (file) {
|
||||
g_log_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
g_log_enabled = false;
|
||||
}
|
||||
|
||||
g_last_flag_check = now;
|
||||
}
|
||||
|
||||
void InitializeThreadFunc(void* args) {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsInitialized() {
|
||||
return g_has_initialized;
|
||||
}
|
||||
|
||||
bool IsLogEnabled() {
|
||||
return g_log_enabled;
|
||||
}
|
||||
|
||||
void LogLine(const char* format, ...) {
|
||||
std::scoped_lock lock{g_log_mutex};
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (g_has_initialized) {
|
||||
RefreshFlags(false);
|
||||
|
||||
if (g_log_enabled) {
|
||||
FILE* file = fopen(FILE_LOG_FILE_PATH, "a");
|
||||
|
||||
if (file) {
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
struct tm* nowTm = localtime(&now.tv_sec);
|
||||
|
||||
fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d.%03ld] ", nowTm->tm_year+1900, nowTm->tm_mon+1, nowTm->tm_mday, nowTm->tm_hour, nowTm->tm_min, nowTm->tm_sec, now.tv_nsec / 1000000UL);
|
||||
vfprintf(file, format, args);
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void FileUtils::WriteContextToCsv(const SysClkContext* context)
|
||||
{
|
||||
std::scoped_lock lock{g_csv_mutex};
|
||||
void WriteContextToCsv(const SysClkContext* context) {
|
||||
std::scoped_lock lock{g_csv_mutex};
|
||||
|
||||
FILE* file = fopen(FILE_CONTEXT_CSV_PATH, "a");
|
||||
FILE* file = fopen(FILE_CONTEXT_CSV_PATH, "a");
|
||||
|
||||
if (file)
|
||||
{
|
||||
// Print header
|
||||
if(!ftell(file))
|
||||
{
|
||||
fprintf(file, "timestamp,profile,app_tid");
|
||||
if (file) {
|
||||
// Print header
|
||||
if (!ftell(file)) {
|
||||
fprintf(file, "timestamp,profile,app_tid");
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
fprintf(file, ",%s_hz", sysclkFormatModule((SysClkModule)module, false));
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) {
|
||||
fprintf(file, ",%s_hz", sysclkFormatModule((SysClkModule)module, false));
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++) {
|
||||
fprintf(file, ",%s_milliC", sysclkFormatThermalSensor((SysClkThermalSensor)sensor, false));
|
||||
}
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) {
|
||||
fprintf(file, ",%s_real_hz", sysclkFormatModule((SysClkModule)module, false));
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkPowerSensor_EnumMax; sensor++) {
|
||||
fprintf(file, ",%s_mw", sysclkFormatPowerSensor((SysClkPowerSensor)sensor, false));
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
fprintf(file, ",%s_milliC", sysclkFormatThermalSensor((SysClkThermalSensor)sensor, false));
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
fprintf(file, "%ld%03ld,%s,%016lx", now.tv_sec, now.tv_nsec / 1000000UL, sysclkFormatProfile(context->profile, false), context->applicationId);
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) {
|
||||
fprintf(file, ",%d", context->freqs[module]);
|
||||
}
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
fprintf(file, ",%s_real_hz", sysclkFormatModule((SysClkModule)module, false));
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++) {
|
||||
fprintf(file, ",%d", context->temps[sensor]);
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkPowerSensor_EnumMax; sensor++)
|
||||
{
|
||||
fprintf(file, ",%s_mw", sysclkFormatPowerSensor((SysClkPowerSensor)sensor, false));
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) {
|
||||
fprintf(file, ",%d", context->realFreqs[module]);
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkPowerSensor_EnumMax; sensor++) {
|
||||
fprintf(file, ",%d", context->power[sensor]);
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
fprintf(file, "%ld%03ld,%s,%016lx", now.tv_sec, now.tv_nsec / 1000000UL, sysclkFormatProfile(context->profile, false), context->applicationId);
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
fprintf(file, ",%d", context->freqs[module]);
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
fprintf(file, ",%d", context->temps[sensor]);
|
||||
}
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
fprintf(file, ",%d", context->realFreqs[module]);
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkPowerSensor_EnumMax; sensor++)
|
||||
{
|
||||
fprintf(file, ",%d", context->power[sensor]);
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
void FileUtils::RefreshFlags(bool force)
|
||||
{
|
||||
std::uint64_t now = armTicksToNs(armGetSystemTick());
|
||||
if(!force && (now - g_last_flag_check) < FILE_FLAG_CHECK_INTERVAL_NS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* file = fopen(FILE_LOG_FLAG_PATH, "r");
|
||||
if (file)
|
||||
{
|
||||
g_log_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
void InitializeAsync() {
|
||||
Thread initThread = {0};
|
||||
threadCreate(&initThread, InitializeThreadFunc, NULL, NULL, 0x4000, 0x15, 0);
|
||||
threadStart(&initThread);
|
||||
}
|
||||
|
||||
Result Initialize() {
|
||||
Result rc = 0;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = timeInitialize();
|
||||
}
|
||||
|
||||
__libnx_init_time();
|
||||
timeExit();
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = fsInitialize();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = fsdevMountSdmc();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
RefreshFlags(true);
|
||||
g_has_initialized = true;
|
||||
LogLine("=== hoc-clk " TARGET_VERSION " ===");
|
||||
LogLine("by m4xw, natinusala, p-sam, Souldbminer and Lightos_");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void Exit() {
|
||||
if (!g_has_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_has_initialized = false;
|
||||
g_log_enabled = false;
|
||||
|
||||
fsdevUnmountAll();
|
||||
fsExit();
|
||||
}
|
||||
|
||||
g_last_flag_check = now;
|
||||
}
|
||||
|
||||
void FileUtils::InitializeAsync()
|
||||
{
|
||||
Thread initThread = {0};
|
||||
threadCreate(&initThread, _FileUtils_InitializeThreadFunc, NULL, NULL, 0x4000, 0x15, 0);
|
||||
threadStart(&initThread);
|
||||
}
|
||||
|
||||
Result FileUtils::Initialize()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = timeInitialize();
|
||||
}
|
||||
|
||||
__libnx_init_time();
|
||||
timeExit();
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = fsInitialize();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = fsdevMountSdmc();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
FileUtils::RefreshFlags(true);
|
||||
g_has_initialized = true;
|
||||
FileUtils::LogLine("=== hoc-clk " TARGET_VERSION " ===");
|
||||
FileUtils::LogLine("by m4xw, natinusala, p-sam, Souldbminer and Lightos_");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void FileUtils::Exit()
|
||||
{
|
||||
if (!g_has_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_has_initialized = false;
|
||||
g_log_enabled = false;
|
||||
|
||||
fsdevUnmountAll();
|
||||
fsExit();
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
@@ -24,8 +24,8 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
@@ -40,16 +40,14 @@
|
||||
#define FILE_LOG_FLAG_PATH FILE_CONFIG_DIR "/log.flag"
|
||||
#define FILE_LOG_FILE_PATH FILE_CONFIG_DIR "/log.txt"
|
||||
|
||||
class FileUtils
|
||||
{
|
||||
public:
|
||||
static void Exit();
|
||||
static Result Initialize();
|
||||
static bool IsInitialized();
|
||||
static bool IsLogEnabled();
|
||||
static void InitializeAsync();
|
||||
static void LogLine(const char* format, ...);
|
||||
static void WriteContextToCsv(const SysClkContext* context);
|
||||
protected:
|
||||
static void RefreshFlags(bool force);
|
||||
};
|
||||
namespace fileUtils {
|
||||
|
||||
void Exit();
|
||||
Result Initialize();
|
||||
bool IsInitialized();
|
||||
bool IsLogEnabled();
|
||||
void InitializeAsync();
|
||||
void LogLine(const char* format, ...);
|
||||
void WriteContextToCsv(const SysClkContext* context);
|
||||
|
||||
}
|
||||
349
Source/rewrite-hoc-clk/sysmodule/src/governor.cpp
Normal file
349
Source/rewrite-hoc-clk/sysmodule/src/governor.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
#include "governor.hpp"
|
||||
|
||||
namespace governor {
|
||||
|
||||
#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 isCpuGovernorEnabled = false;
|
||||
bool lastGpuGovernorState = false;
|
||||
bool lastCpuGovernorState = false;
|
||||
bool lastVrrGovernorState = false;
|
||||
bool hasChanged = true;
|
||||
bool isCpuGovernorInBoostMode = false;
|
||||
bool isVRREnabled = false;
|
||||
|
||||
// thread handles
|
||||
Thread cpuGovernorTHREAD;
|
||||
Thread gpuGovernorTHREAD;
|
||||
Thread vrrTHREAD;
|
||||
|
||||
void HandleGovernor(uint32_t targetHz)
|
||||
{
|
||||
u32 tempTargetHz = clockManager::gContext.overrideFreqs[HorizonOCModule_Governor];
|
||||
if (!tempTargetHz) {
|
||||
tempTargetHz = config::GetAutoClockHz(clockManager::gContext.applicationId, HorizonOCModule_Governor, clockManager::gContext.profile, true);
|
||||
if (!tempTargetHz)
|
||||
tempTargetHz = config::GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Governor, clockManager::gContext.profile, true);
|
||||
}
|
||||
|
||||
auto resolve = [](u8 app, u8 temp) -> u8 {
|
||||
if (temp == ComponentGovernor_Disabled) return ComponentGovernor_Disabled;
|
||||
if (temp != ComponentGovernor_DoNotOverride) return temp;
|
||||
return app;
|
||||
};
|
||||
|
||||
u8 effectiveCpu = resolve(GovernorStateCpu(targetHz), GovernorStateCpu(tempTargetHz));
|
||||
u8 effectiveGpu = resolve(GovernorStateGpu(targetHz), GovernorStateGpu(tempTargetHz));
|
||||
u8 effectiveVrr = resolve(GovernorStateVrr(targetHz), GovernorStateVrr(tempTargetHz));
|
||||
|
||||
bool newCpuGovernorState = (effectiveCpu == ComponentGovernor_Enabled);
|
||||
bool newGpuGovernorState = (effectiveGpu == ComponentGovernor_Enabled);
|
||||
bool newVrrGovernorState = (effectiveVrr == ComponentGovernor_Enabled);
|
||||
|
||||
isCpuGovernorEnabled = newCpuGovernorState;
|
||||
isGpuGovernorEnabled = newGpuGovernorState;
|
||||
isVRREnabled = newVrrGovernorState;
|
||||
|
||||
if (newCpuGovernorState == false && lastCpuGovernorState == true) {
|
||||
svcSleepThread(100'000'000); // thread syncing. probably a cleaner way to do this but hey, it works!
|
||||
board::ResetToStockCpu();
|
||||
}
|
||||
if (newGpuGovernorState == false && lastGpuGovernorState == true) {
|
||||
svcSleepThread(100'000'000);
|
||||
board::ResetToStockGpu();
|
||||
}
|
||||
if (newVrrGovernorState == false && lastVrrGovernorState == true) {
|
||||
svcSleepThread(100'000'000);
|
||||
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;
|
||||
lastGpuGovernorState = newGpuGovernorState;
|
||||
lastVrrGovernorState = newVrrGovernorState;
|
||||
}
|
||||
}
|
||||
|
||||
u32 SchedutilTargetHz(u32 util, u32 tableMaxHz)
|
||||
{
|
||||
u64 hz = (u64)tableMaxHz * util / STEP_UTIL;
|
||||
return (u32)(std::min(hz, static_cast<u64>(tableMaxHz)));
|
||||
}
|
||||
|
||||
u32 TableIndexForHz(const clockManager::FreqTable& table, u32 targetHz)
|
||||
{
|
||||
for (u32 i = 0; i < table.count; i++)
|
||||
if (table.list[i] >= targetHz)
|
||||
return i;
|
||||
return table.count - 1;
|
||||
}
|
||||
|
||||
u32 ResolveTargetHz(SysClkModule module)
|
||||
{
|
||||
u32 hz = clockManager::gContext.overrideFreqs[module];
|
||||
if (!hz)
|
||||
hz = config::GetAutoClockHz(
|
||||
clockManager::gContext.applicationId, module,
|
||||
clockManager::gContext.profile, false);
|
||||
if (!hz)
|
||||
hz = config::GetAutoClockHz(
|
||||
GLOBAL_PROFILE_ID, module,
|
||||
clockManager::gContext.profile, false);
|
||||
return hz;
|
||||
}
|
||||
|
||||
void CpuGovernorThread(void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
u32 downHoldRemaining = 0;
|
||||
u32 lastHz = 0;
|
||||
u32 minHz = 612;
|
||||
u32 tick = 0;
|
||||
for (;;) {
|
||||
if (!clockManager::gRunning || !isCpuGovernorEnabled) {
|
||||
downHoldRemaining = 0;
|
||||
lastHz = 0;
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 mode = 0;
|
||||
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
|
||||
|
||||
if (R_SUCCEEDED(rc) && apmExtIsBoostMode(mode)) {
|
||||
isCpuGovernorInBoostMode = true;
|
||||
downHoldRemaining = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
auto& table = clockManager::gFreqTable[SysClkModule_CPU];
|
||||
|
||||
if (table.count == 0)
|
||||
continue;
|
||||
|
||||
std::scoped_lock lock{clockManager::gContextMutex};
|
||||
|
||||
u32 cpuLoad = board::GetPartLoad(HocClkPartLoad_CPUMax);
|
||||
|
||||
u32 tableMaxHz = table.list[table.count - 1];
|
||||
u32 desiredHz = SchedutilTargetHz(cpuLoad, tableMaxHz);
|
||||
u32 targetHz = ResolveTargetHz(SysClkModule_CPU);
|
||||
u32 maxHz = clockManager::GetMaxAllowedHz(SysClkModule_CPU, clockManager::gContext.profile);
|
||||
|
||||
if (targetHz && desiredHz > targetHz)
|
||||
desiredHz = targetHz;
|
||||
|
||||
if (maxHz && desiredHz > maxHz)
|
||||
desiredHz = maxHz;
|
||||
|
||||
u32 newHz = table.list[TableIndexForHz(table, desiredHz)];
|
||||
|
||||
// ramp up fast, go down slow
|
||||
bool goingDown = (lastHz != 0) && (newHz < lastHz);
|
||||
|
||||
if (!goingDown)
|
||||
downHoldRemaining = 0;
|
||||
else if (downHoldRemaining == 0)
|
||||
downHoldRemaining = DOWN_HOLD_TICKS;
|
||||
|
||||
if (downHoldRemaining > 0)
|
||||
downHoldRemaining--;
|
||||
|
||||
if (++tick > 50) {
|
||||
minHz = config::GetConfigValue(HorizonOCConfigValue_CpuGovernorMinimumFreq);
|
||||
tick = 0;
|
||||
}
|
||||
|
||||
if (newHz < minHz)
|
||||
newHz = minHz;
|
||||
|
||||
if ((!goingDown || (downHoldRemaining == 0)) && clockManager::IsAssignableHz(SysClkModule_CPU, newHz)) {
|
||||
board::SetHz(SysClkModule_CPU, newHz);
|
||||
clockManager::gContext.freqs[SysClkModule_CPU] = newHz;
|
||||
lastHz = newHz;
|
||||
}
|
||||
|
||||
svcSleepThread(POLL_NS);
|
||||
}
|
||||
}
|
||||
|
||||
void GovernorThread(void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
u32 downHoldRemaining = 0;
|
||||
u32 lastHz = 0;
|
||||
|
||||
for (;;) {
|
||||
if (!clockManager::gRunning || !isGpuGovernorEnabled) {
|
||||
downHoldRemaining = 0;
|
||||
lastHz = 0;
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& table = clockManager::gFreqTable[SysClkModule_GPU];
|
||||
if (table.count == 0)
|
||||
continue;
|
||||
|
||||
std::scoped_lock lock{clockManager::gContextMutex};
|
||||
|
||||
u32 gpuLoad = board::GetPartLoad(HocClkPartLoad_GPU);
|
||||
u32 tableMaxHz = table.list[table.count - 1];
|
||||
u32 desiredHz = SchedutilTargetHz(gpuLoad, tableMaxHz);
|
||||
u32 targetHz = ResolveTargetHz(SysClkModule_GPU);
|
||||
u32 maxHz = clockManager::GetMaxAllowedHz(SysClkModule_GPU, clockManager::gContext.profile);
|
||||
|
||||
if (targetHz && desiredHz > targetHz)
|
||||
desiredHz = targetHz;
|
||||
|
||||
if (maxHz && desiredHz > maxHz)
|
||||
desiredHz = maxHz;
|
||||
|
||||
u32 newHz = table.list[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)) && clockManager::IsAssignableHz(SysClkModule_GPU, newHz)) {
|
||||
board::SetHz(SysClkModule_GPU, newHz);
|
||||
clockManager::gContext.freqs[SysClkModule_GPU] = newHz;
|
||||
lastHz = newHz;
|
||||
}
|
||||
|
||||
svcSleepThread(POLL_NS);
|
||||
}
|
||||
}
|
||||
|
||||
void VRRThread(void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
u8 tick = 0;
|
||||
for (;;) {
|
||||
if (!clockManager::gRunning || clockManager::gContext.profile == SysClkProfile_Docked || !isVRREnabled) {
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{clockManager::gContextMutex};
|
||||
|
||||
u8 fps;
|
||||
|
||||
if (clockManager::gContext.isSaltyNXInstalled) {
|
||||
fps = integrations::GetSaltyNXFPS();
|
||||
} else {
|
||||
svcSleepThread(~0ULL); // effectively disable the thread if SaltyNX isn't installed, as there's no point in it running
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fps == 254) {
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
// if(appletGetFocusState() != AppletFocusState_InFocus) {
|
||||
// board::ResetToStockDisplay();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
u32 targetHz = clockManager::gContext.overrideFreqs[HorizonOCModule_Display];
|
||||
if (!targetHz) {
|
||||
targetHz = config::GetAutoClockHz(clockManager::gContext.applicationId, HorizonOCModule_Display, clockManager::gContext.profile, false);
|
||||
if (!targetHz)
|
||||
targetHz = config::GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Display, clockManager::gContext.profile, false);
|
||||
}
|
||||
|
||||
u8 maxDisplay;
|
||||
if (targetHz) {
|
||||
maxDisplay = targetHz;
|
||||
} else {
|
||||
maxDisplay = 60; // don't assume display stuff!
|
||||
}
|
||||
|
||||
u8 minDisplay = board::GetConsoleType() == HorizonOCConsoleType_Aula ? 45 : 40;
|
||||
if (maxDisplay == minDisplay)
|
||||
continue;
|
||||
|
||||
if (fps >= minDisplay && fps <= maxDisplay) {
|
||||
board::SetHz(HorizonOCModule_Display, fps);
|
||||
clockManager::gContext.freqs[HorizonOCModule_Display] = fps;
|
||||
clockManager::gContext.realFreqs[HorizonOCModule_Display] = fps;
|
||||
} else {
|
||||
for (u32 i = 0; i < 10; i++) {
|
||||
u32 compareHz = fps * i;
|
||||
if (compareHz >= minDisplay && compareHz <= maxDisplay) {
|
||||
board::SetHz(HorizonOCModule_Display, compareHz);
|
||||
clockManager::gContext.freqs[HorizonOCModule_Display] = compareHz;
|
||||
clockManager::gContext.realFreqs[HorizonOCModule_Display] = compareHz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (++tick > 50) {
|
||||
board::SetHz(HorizonOCModule_Display, maxDisplay);
|
||||
tick = 0;
|
||||
svcSleepThread(50'000'000);
|
||||
}
|
||||
|
||||
svcSleepThread(POLL_NS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void startThreads() {
|
||||
|
||||
threadCreate(
|
||||
&cpuGovernorTHREAD,
|
||||
CpuGovernorThread,
|
||||
nullptr,
|
||||
NULL,
|
||||
0x2000,
|
||||
0x3F,
|
||||
-2
|
||||
);
|
||||
|
||||
threadCreate(
|
||||
&gpuGovernorTHREAD,
|
||||
GovernorThread,
|
||||
nullptr,
|
||||
NULL,
|
||||
0x2000,
|
||||
0x3F,
|
||||
-2
|
||||
);
|
||||
|
||||
threadCreate(
|
||||
&vrrTHREAD,
|
||||
VRRThread,
|
||||
nullptr,
|
||||
NULL,
|
||||
0x2000,
|
||||
0x3F,
|
||||
-2
|
||||
);
|
||||
|
||||
threadStart(&cpuGovernorTHREAD);
|
||||
threadStart(&gpuGovernorTHREAD);
|
||||
threadStart(&vrrTHREAD);
|
||||
}
|
||||
|
||||
void exitThreads() {
|
||||
threadClose(&cpuGovernorTHREAD);
|
||||
threadClose(&gpuGovernorTHREAD);
|
||||
threadClose(&vrrTHREAD);
|
||||
}
|
||||
}
|
||||
27
Source/rewrite-hoc-clk/sysmodule/src/governor.hpp
Normal file
27
Source/rewrite-hoc-clk/sysmodule/src/governor.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <switch.h>
|
||||
#include <sysclk.h>
|
||||
#include "board/board.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include <cstring>
|
||||
#include "file_utils.hpp"
|
||||
#include "board/board.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "config.hpp"
|
||||
#include "integrations.hpp"
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
|
||||
namespace governor {
|
||||
extern bool isCpuGovernorInBoostMode;
|
||||
extern bool isVRREnabled;
|
||||
extern bool isGpuGovernorEnabled;
|
||||
extern bool isCpuGovernorEnabled;
|
||||
extern bool lastGpuGovernorState;
|
||||
extern bool lastCpuGovernorState;
|
||||
extern bool lastVrrGovernorState;
|
||||
void startThreads();
|
||||
void exitThreads();
|
||||
void HandleGovernor(uint32_t targetHz);
|
||||
void CpuGovernorThread(void* arg);
|
||||
void GovernorThread(void* arg);
|
||||
void VRRThread(void* arg);
|
||||
}
|
||||
@@ -15,123 +15,137 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "integrations.h"
|
||||
#include "integrations.hpp"
|
||||
#include <sys/stat.h>
|
||||
#include <SaltyNX.h>
|
||||
#include "process_management.hpp"
|
||||
|
||||
SysDockIntegration::SysDockIntegration() {
|
||||
}
|
||||
namespace integrations {
|
||||
|
||||
bool SysDockIntegration::getCurrentSysDockState() {
|
||||
struct stat st = {0};
|
||||
return stat("sdmc:/atmosphere/contents/42000000000000A0/flags/boot2.flag", &st) == 0;
|
||||
}
|
||||
namespace {
|
||||
|
||||
SaltyNXIntegration::SaltyNXIntegration() {
|
||||
}
|
||||
NxFpsSharedBlock* gNxFps = nullptr;
|
||||
SharedMemory gSharedMemory = {};
|
||||
bool gSharedMemoryUsed = false;
|
||||
Handle gRemoteSharedMemory = 1;
|
||||
u64 gPrevTid = 0;
|
||||
|
||||
void SaltyNXIntegration::LoadSaltyNX() {
|
||||
if (!CheckPort())
|
||||
return;
|
||||
LoadSharedMemory();
|
||||
}
|
||||
bool CheckSaltyNXPort() {
|
||||
Handle saltysd;
|
||||
|
||||
bool SaltyNXIntegration::getCurrentSaltyNXState() {
|
||||
struct stat st = {0};
|
||||
return stat("sdmc:/atmosphere/contents/0000000000534C56/flags/boot2.flag", &st) == 0;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
bool SaltyNXIntegration::CheckPort() {
|
||||
Handle saltysd;
|
||||
for (int i = 0; i < 67; i++) {
|
||||
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||
svcCloseHandle(saltysd);
|
||||
return true;
|
||||
}
|
||||
svcSleepThread(1'000'000);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 67; i++) {
|
||||
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||
svcCloseHandle(saltysd);
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
if (i == 66) return false;
|
||||
svcSleepThread(1'000'000);
|
||||
|
||||
void SearchSharedMemoryBlock(uintptr_t base) {
|
||||
ptrdiff_t search_offset = 0;
|
||||
while (search_offset < 0x1000) {
|
||||
gNxFps = (NxFpsSharedBlock*)(base + search_offset);
|
||||
if (gNxFps->MAGIC == 0x465053)
|
||||
return;
|
||||
search_offset += 4;
|
||||
}
|
||||
gNxFps = nullptr;
|
||||
}
|
||||
|
||||
void LoadSharedMemory() {
|
||||
if (SaltySD_Connect())
|
||||
return;
|
||||
SaltySD_GetSharedMemoryHandle(&gRemoteSharedMemory);
|
||||
SaltySD_Term();
|
||||
shmemLoadRemote(&gSharedMemory, gRemoteSharedMemory, 0x1000, Perm_Rw);
|
||||
if (!shmemMap(&gSharedMemory))
|
||||
gSharedMemoryUsed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < 67; i++) {
|
||||
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||
svcCloseHandle(saltysd);
|
||||
return true;
|
||||
}
|
||||
svcSleepThread(1'000'000);
|
||||
bool GetSysDockState() {
|
||||
struct stat st = {0};
|
||||
return stat("sdmc:/atmosphere/contents/42000000000000A0/flags/boot2.flag", &st) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool GetSaltyNXState() {
|
||||
struct stat st = {0};
|
||||
return stat("sdmc:/atmosphere/contents/0000000000534C56/flags/boot2.flag", &st) == 0;
|
||||
}
|
||||
|
||||
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)
|
||||
void LoadSaltyNX() {
|
||||
if (!CheckSaltyNXPort())
|
||||
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;
|
||||
LoadSharedMemory();
|
||||
}
|
||||
|
||||
if (!NxFps) {
|
||||
uintptr_t base = (uintptr_t)shmemGetAddr(&_sharedmemory);
|
||||
searchSharedMemoryBlock(base);
|
||||
u8 GetSaltyNXFPS() {
|
||||
if (!gSharedMemoryUsed)
|
||||
return 254;
|
||||
|
||||
u64 tid = processManagement::GetCurrentApplicationId();
|
||||
if (tid == 0)
|
||||
return 254;
|
||||
|
||||
if (gPrevTid != tid) {
|
||||
gNxFps = nullptr;
|
||||
gPrevTid = tid;
|
||||
}
|
||||
|
||||
if (!gNxFps) {
|
||||
uintptr_t base = (uintptr_t)shmemGetAddr(&gSharedMemory);
|
||||
SearchSharedMemoryBlock(base);
|
||||
}
|
||||
|
||||
return gNxFps ? gNxFps->FPS : 254;
|
||||
}
|
||||
|
||||
return NxFps ? NxFps->FPS : 254;
|
||||
}
|
||||
u16 GetSaltyNXResolutionHeight() {
|
||||
if (!gSharedMemoryUsed)
|
||||
return 0;
|
||||
|
||||
u16 SaltyNXIntegration::GetResolutionHeight() {
|
||||
if (!SharedMemoryUsed)
|
||||
u64 tid = processManagement::GetCurrentApplicationId();
|
||||
if (tid == 0)
|
||||
return 0;
|
||||
|
||||
if (gPrevTid != tid) {
|
||||
gNxFps = nullptr;
|
||||
gPrevTid = tid;
|
||||
}
|
||||
|
||||
if (!gNxFps) {
|
||||
uintptr_t base = (uintptr_t)shmemGetAddr(&gSharedMemory);
|
||||
SearchSharedMemoryBlock(base);
|
||||
}
|
||||
|
||||
if (gNxFps) {
|
||||
gNxFps->renderCalls[0].calls = 0xFFFF;
|
||||
svcSleepThread(10*1000);
|
||||
return gNxFps->renderCalls[0].height == 0 ? gNxFps->viewportCalls[0].height : gNxFps->renderCalls[0].height;
|
||||
}
|
||||
return 0;
|
||||
|
||||
u64 tid = processManagement::GetCurrentApplicationId();
|
||||
if (tid == 0)
|
||||
return 0;
|
||||
|
||||
if (prevTid != tid) {
|
||||
NxFps = 0;
|
||||
prevTid = tid;
|
||||
}
|
||||
|
||||
if (!NxFps) {
|
||||
uintptr_t base = (uintptr_t)shmemGetAddr(&_sharedmemory);
|
||||
searchSharedMemoryBlock(base);
|
||||
}
|
||||
if(NxFps) {
|
||||
NxFps->renderCalls[0].calls = 0xFFFF;
|
||||
svcSleepThread(10*1000);
|
||||
|
||||
return NxFps->renderCalls[0].height == 0 ? NxFps->viewportCalls[0].height : NxFps->renderCalls[0].height;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,37 +12,24 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <nxExt.h>
|
||||
#include <sysclk.h>
|
||||
|
||||
#include <switch.h>
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
#include <sysclk.h>
|
||||
|
||||
#include "clock_manager.h"
|
||||
|
||||
class SysDockIntegration {
|
||||
public:
|
||||
SysDockIntegration();
|
||||
|
||||
bool getCurrentSysDockState();
|
||||
};
|
||||
|
||||
class SaltyNXIntegration {
|
||||
public:
|
||||
struct resolutionCalls {
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t calls;
|
||||
};
|
||||
namespace integrations {
|
||||
|
||||
struct NxFpsSharedBlock {
|
||||
uint32_t MAGIC;
|
||||
@@ -60,15 +47,18 @@ public:
|
||||
uint8_t ActiveBuffers;
|
||||
uint8_t SetActiveBuffers;
|
||||
union {
|
||||
struct {
|
||||
struct {
|
||||
bool handheld: 1;
|
||||
bool docked: 1;
|
||||
unsigned int reserved: 6;
|
||||
} NX_PACKED ds;
|
||||
uint8_t general;
|
||||
} displaySync;
|
||||
resolutionCalls renderCalls[8];
|
||||
resolutionCalls viewportCalls[8];
|
||||
struct resolutionCalls {
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t calls;
|
||||
} renderCalls[8], viewportCalls[8];
|
||||
bool forceOriginalRefreshRate;
|
||||
bool dontForce60InDocked;
|
||||
bool forceSuspend;
|
||||
@@ -78,17 +68,10 @@ public:
|
||||
uint64_t frameNumber;
|
||||
} NX_PACKED;
|
||||
|
||||
NxFpsSharedBlock* NxFps = 0;
|
||||
SharedMemory _sharedmemory = {};
|
||||
bool SharedMemoryUsed = false;
|
||||
Handle remoteSharedMemory = 1;
|
||||
SaltyNXIntegration();
|
||||
bool GetSysDockState();
|
||||
bool GetSaltyNXState();
|
||||
void LoadSaltyNX();
|
||||
bool getCurrentSaltyNXState();
|
||||
u8 GetSaltyNXFPS();
|
||||
u16 GetSaltyNXResolutionHeight();
|
||||
|
||||
bool CheckPort();
|
||||
void LoadSharedMemory();
|
||||
void searchSharedMemoryBlock(uintptr_t base);
|
||||
u8 GetFPS();
|
||||
u16 GetResolutionHeight();
|
||||
};
|
||||
}
|
||||
@@ -24,351 +24,271 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "ipc_service.h"
|
||||
#include "ipc_service.hpp"
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
#include "file_utils.h"
|
||||
#include "errors.h"
|
||||
#include "clock_manager.h"
|
||||
IpcService::IpcService(ClockManager* clockMgr)
|
||||
{
|
||||
std::int32_t priority;
|
||||
Result rc = svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
|
||||
ASSERT_RESULT_OK(rc, "svcGetThreadPriority");
|
||||
rc = ipcServerInit(&this->server, SYSCLK_IPC_SERVICE_NAME, 42);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerInit");
|
||||
rc = threadCreate(&this->thread, &IpcService::ProcessThreadFunc, this, NULL, 0x2000, priority, -2);
|
||||
ASSERT_RESULT_OK(rc, "threadCreate");
|
||||
#include <nxExt.h>
|
||||
#include "file_utils.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include "config.hpp"
|
||||
#include "kip.hpp"
|
||||
namespace ipcService {
|
||||
|
||||
this->running = false;
|
||||
this->clockMgr = clockMgr;
|
||||
namespace {
|
||||
|
||||
}
|
||||
bool gRunning = false;
|
||||
Thread gThread;
|
||||
LockableMutex gThreadMutex;
|
||||
IpcServer gServer;
|
||||
|
||||
void IpcService::SetRunning(bool running)
|
||||
{
|
||||
std::scoped_lock lock{this->threadMutex};
|
||||
if(this->running == running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Result GetApiVersion(u32* out_version) {
|
||||
*out_version = SYSCLK_IPC_API_VERSION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
this->running = running;
|
||||
|
||||
if(running)
|
||||
{
|
||||
Result rc = threadStart(&this->thread);
|
||||
ASSERT_RESULT_OK(rc, "threadStart");
|
||||
}
|
||||
else
|
||||
{
|
||||
svcCancelSynchronization(this->thread.handle);
|
||||
threadWaitForExit(&this->thread);
|
||||
}
|
||||
}
|
||||
|
||||
IpcService::~IpcService()
|
||||
{
|
||||
this->SetRunning(false);
|
||||
Result rc = threadClose(&this->thread);
|
||||
ASSERT_RESULT_OK(rc, "threadClose");
|
||||
rc = ipcServerExit(&this->server);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerExit");
|
||||
}
|
||||
|
||||
void IpcService::ProcessThreadFunc(void* arg)
|
||||
{
|
||||
Result rc;
|
||||
IpcService* ipcSrv = (IpcService*)arg;
|
||||
while(true)
|
||||
{
|
||||
rc = ipcServerProcess(&ipcSrv->server, &IpcService::ServiceHandlerFunc, arg);
|
||||
if(R_FAILED(rc))
|
||||
{
|
||||
if(rc == KERNELRESULT(Cancelled))
|
||||
{
|
||||
return;
|
||||
Result GetVersionString(char* out_buf, size_t bufSize) {
|
||||
if (bufSize) {
|
||||
strncpy(out_buf, TARGET_VERSION, bufSize-1);
|
||||
}
|
||||
if(rc != KERNELRESULT(ConnectionClosed))
|
||||
{
|
||||
FileUtils::LogLine("[ipc] ipcServerProcess: [0x%x] %04d-%04d", rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetCurrentContext(SysClkContext* out_ctx) {
|
||||
*out_ctx = clockManager::GetCurrentContext();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result ExitHandler() {
|
||||
clockManager::SetRunning(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
*out_count = config::GetProfileCount(*tid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
config::GetProfiles(*tid, out_profiles);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result SetProfiles(SysClkIpc_SetProfiles_Args* args) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
SysClkTitleProfileList profiles = args->profiles;
|
||||
if (!config::SetProfiles(args->tid, &profiles, true)) {
|
||||
return SYSCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result SetEnabled(std::uint8_t* enabled) {
|
||||
config::SetEnabled(*enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result SetOverride(SysClkIpc_SetOverride_Args* args) {
|
||||
if (!SYSCLK_ENUM_VALID(SysClkModule, args->module)) {
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
config::SetOverrideHz(args->module, args->hz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetConfigValuesHandler(SysClkConfigValueList* out_configValues) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
config::GetConfigValues(out_configValues);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result SetConfigValuesHandler(SysClkConfigValueList* configValues) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
SysClkConfigValueList copy = *configValues;
|
||||
if (!config::SetConfigValues(©, true)) {
|
||||
return SYSCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetFreqList(SysClkIpc_GetFreqList_Args* args, std::uint32_t* out_list, std::size_t size, std::uint32_t* out_count) {
|
||||
if (!SYSCLK_ENUM_VALID(SysClkModule, args->module)) {
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
if (args->maxCount != size/sizeof(*out_list)) {
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
clockManager::GetFreqList(args->module, out_list, args->maxCount, out_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8* out_data, size_t* out_dataSize) {
|
||||
(void)arg;
|
||||
switch (r->data.cmdId) {
|
||||
case SysClkIpcCmd_GetApiVersion:
|
||||
*out_dataSize = sizeof(u32);
|
||||
return GetApiVersion((u32*)out_data);
|
||||
|
||||
case SysClkIpcCmd_GetVersionString:
|
||||
if (r->hipc.meta.num_recv_buffers >= 1) {
|
||||
return GetVersionString(
|
||||
(char*)hipcGetBufferAddress(r->hipc.data.recv_buffers),
|
||||
hipcGetBufferSize(r->hipc.data.recv_buffers)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetCurrentContext:
|
||||
if (r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1) {
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||
if (bufSize >= sizeof(SysClkContext)) {
|
||||
return GetCurrentContext((SysClkContext*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_Exit:
|
||||
return ExitHandler();
|
||||
|
||||
case SysClkIpcCmd_GetProfileCount:
|
||||
if (r->data.size >= sizeof(std::uint64_t)) {
|
||||
*out_dataSize = sizeof(std::uint8_t);
|
||||
return GetProfileCount((std::uint64_t*)r->data.ptr, (std::uint8_t*)out_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetProfiles:
|
||||
if (r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1) {
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||
if (bufSize >= sizeof(SysClkTitleProfileList)) {
|
||||
return GetProfiles((std::uint64_t*)r->data.ptr, (SysClkTitleProfileList*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetProfiles:
|
||||
if (r->data.size >= sizeof(SysClkIpc_SetProfiles_Args)) {
|
||||
return SetProfiles((SysClkIpc_SetProfiles_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetEnabled:
|
||||
if (r->data.size >= sizeof(std::uint8_t)) {
|
||||
return SetEnabled((std::uint8_t*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetOverride:
|
||||
if (r->data.size >= sizeof(SysClkIpc_SetOverride_Args)) {
|
||||
return SetOverride((SysClkIpc_SetOverride_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetConfigValues:
|
||||
if (r->hipc.meta.num_recv_buffers >= 1) {
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||
if (bufSize >= sizeof(SysClkConfigValueList)) {
|
||||
return GetConfigValuesHandler((SysClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetConfigValues:
|
||||
if (r->hipc.meta.num_send_buffers >= 1) {
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.send_buffers);
|
||||
if (bufSize >= sizeof(SysClkConfigValueList)) {
|
||||
return SetConfigValuesHandler((SysClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.send_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetFreqList:
|
||||
if (r->data.size >= sizeof(SysClkIpc_GetFreqList_Args) && r->hipc.meta.num_recv_buffers >= 1) {
|
||||
*out_dataSize = sizeof(std::uint32_t);
|
||||
return GetFreqList(
|
||||
(SysClkIpc_GetFreqList_Args*)r->data.ptr,
|
||||
(std::uint32_t*)hipcGetBufferAddress(r->hipc.data.recv_buffers),
|
||||
hipcGetBufferSize(r->hipc.data.recv_buffers),
|
||||
(std::uint32_t*)out_data
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_SetKipData:
|
||||
if (r->data.size >= 0) {
|
||||
kip::SetKipData();
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
void ProcessThreadFunc(void* arg) {
|
||||
(void)arg;
|
||||
Result rc;
|
||||
while (true) {
|
||||
rc = ipcServerProcess(&gServer, &ServiceHandlerFunc, nullptr);
|
||||
if (R_FAILED(rc)) {
|
||||
if (rc == KERNELRESULT(Cancelled)) {
|
||||
return;
|
||||
}
|
||||
if (rc != KERNELRESULT(ConnectionClosed)) {
|
||||
fileUtils::LogLine("[ipc] ipcServerProcess: [0x%x] %04d-%04d", rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8* out_data, size_t* out_dataSize)
|
||||
{
|
||||
IpcService* ipcSrv = (IpcService*)arg;
|
||||
|
||||
switch(r->data.cmdId)
|
||||
{
|
||||
case SysClkIpcCmd_GetApiVersion:
|
||||
*out_dataSize = sizeof(u32);
|
||||
return ipcSrv->GetApiVersion((u32*)out_data);
|
||||
|
||||
case SysClkIpcCmd_GetVersionString:
|
||||
if(r->hipc.meta.num_recv_buffers >= 1)
|
||||
{
|
||||
return ipcSrv->GetVersionString(
|
||||
(char*)hipcGetBufferAddress(r->hipc.data.recv_buffers),
|
||||
hipcGetBufferSize(r->hipc.data.recv_buffers)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetCurrentContext:
|
||||
if(r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1)
|
||||
{
|
||||
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:
|
||||
return ipcSrv->Exit();
|
||||
|
||||
case SysClkIpcCmd_GetProfileCount:
|
||||
if(r->data.size >= sizeof(std::uint64_t))
|
||||
{
|
||||
*out_dataSize = sizeof(std::uint8_t);
|
||||
return ipcSrv->GetProfileCount((std::uint64_t*)r->data.ptr, (std::uint8_t*)out_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetProfiles:
|
||||
if(r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1)
|
||||
{
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||
if(bufSize >= sizeof(SysClkTitleProfileList))
|
||||
{
|
||||
return ipcSrv->GetProfiles((std::uint64_t*)r->data.ptr, (SysClkTitleProfileList*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetProfiles:
|
||||
if(r->data.size >= sizeof(SysClkIpc_SetProfiles_Args))
|
||||
{
|
||||
return ipcSrv->SetProfiles((SysClkIpc_SetProfiles_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetEnabled:
|
||||
if(r->data.size >= sizeof(std::uint8_t))
|
||||
{
|
||||
return ipcSrv->SetEnabled((std::uint8_t*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetOverride:
|
||||
if(r->data.size >= sizeof(SysClkIpc_SetOverride_Args))
|
||||
{
|
||||
return ipcSrv->SetOverride((SysClkIpc_SetOverride_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetConfigValues:
|
||||
if(r->hipc.meta.num_recv_buffers >= 1)
|
||||
{
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||
if(bufSize >= sizeof(SysClkConfigValueList))
|
||||
{
|
||||
return ipcSrv->GetConfigValues((SysClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetConfigValues:
|
||||
if(r->hipc.meta.num_send_buffers >= 1)
|
||||
{
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.send_buffers);
|
||||
if(bufSize >= sizeof(SysClkConfigValueList))
|
||||
{
|
||||
return ipcSrv->SetConfigValues((SysClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.send_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SysClkIpcCmd_GetFreqList:
|
||||
if(r->data.size >= sizeof(SysClkIpc_GetFreqList_Args) && r->hipc.meta.num_recv_buffers >= 1)
|
||||
{
|
||||
*out_dataSize = sizeof(std::uint32_t);
|
||||
return ipcSrv->GetFreqList(
|
||||
(SysClkIpc_GetFreqList_Args*)r->data.ptr,
|
||||
(std::uint32_t*)hipcGetBufferAddress(r->hipc.data.recv_buffers),
|
||||
hipcGetBufferSize(r->hipc.data.recv_buffers),
|
||||
(std::uint32_t*)out_data
|
||||
);
|
||||
}
|
||||
break;
|
||||
case HocClkIpcCmd_SetKipData:
|
||||
if (r->data.size >= 0) {
|
||||
return ipcSrv->SetKipData();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
Result IpcService::GetApiVersion(u32* out_version)
|
||||
{
|
||||
*out_version = SYSCLK_IPC_API_VERSION;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetVersionString(char* out_buf, size_t bufSize)
|
||||
{
|
||||
if(bufSize)
|
||||
{
|
||||
strncpy(out_buf, TARGET_VERSION, bufSize-1);
|
||||
void Initialize() {
|
||||
std::int32_t priority;
|
||||
Result rc = svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
|
||||
ASSERT_RESULT_OK(rc, "svcGetThreadPriority");
|
||||
rc = ipcServerInit(&gServer, SYSCLK_IPC_SERVICE_NAME, 42);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerInit");
|
||||
rc = threadCreate(&gThread, &ProcessThreadFunc, nullptr, NULL, 0x2000, priority, -2);
|
||||
ASSERT_RESULT_OK(rc, "threadCreate");
|
||||
gRunning = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetCurrentContext(SysClkContext* out_ctx)
|
||||
{
|
||||
*out_ctx = this->clockMgr->GetCurrentContext();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::Exit()
|
||||
{
|
||||
this->clockMgr->SetRunning(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count)
|
||||
{
|
||||
Config* config = this->clockMgr->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
void Exit() {
|
||||
SetRunning(false);
|
||||
Result rc = threadClose(&gThread);
|
||||
ASSERT_RESULT_OK(rc, "threadClose");
|
||||
rc = ipcServerExit(&gServer);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerExit");
|
||||
}
|
||||
|
||||
*out_count = config->GetProfileCount(*tid);
|
||||
void SetRunning(bool running) {
|
||||
std::scoped_lock lock{gThreadMutex};
|
||||
if (gRunning == running) {
|
||||
return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
gRunning = running;
|
||||
|
||||
Result IpcService::GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles)
|
||||
{
|
||||
Config* config = this->clockMgr->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
if (running) {
|
||||
Result rc = threadStart(&gThread);
|
||||
ASSERT_RESULT_OK(rc, "threadStart");
|
||||
} else {
|
||||
svcCancelSynchronization(gThread.handle);
|
||||
threadWaitForExit(&gThread);
|
||||
}
|
||||
}
|
||||
|
||||
config->GetProfiles(*tid, out_profiles);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetProfiles(SysClkIpc_SetProfiles_Args* args)
|
||||
{
|
||||
Config* config = this->clockMgr->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
SysClkTitleProfileList profiles = args->profiles;
|
||||
|
||||
if(!config->SetProfiles(args->tid, &profiles, true))
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetEnabled(std::uint8_t* enabled)
|
||||
{
|
||||
Config* config = this->clockMgr->GetConfig();
|
||||
config->SetEnabled(*enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetOverride(SysClkIpc_SetOverride_Args* args)
|
||||
{
|
||||
SysClkModule module = args->module;
|
||||
std::uint32_t hz = args->hz;
|
||||
|
||||
if(!SYSCLK_ENUM_VALID(SysClkModule, args->module))
|
||||
{
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
Config* config = this->clockMgr->GetConfig();
|
||||
config->SetOverrideHz(module, hz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetConfigValues(SysClkConfigValueList* out_configValues)
|
||||
{
|
||||
Config* config = this->clockMgr->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
config->GetConfigValues(out_configValues);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetConfigValues(SysClkConfigValueList* configValues)
|
||||
{
|
||||
Config* config = this->clockMgr->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
SysClkConfigValueList configValuesCopy = *configValues;
|
||||
|
||||
if(!config->SetConfigValues(&configValuesCopy, true))
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetFreqList(SysClkIpc_GetFreqList_Args* args, std::uint32_t* out_list, std::size_t size, std::uint32_t* out_count)
|
||||
{
|
||||
if(!SYSCLK_ENUM_VALID(SysClkModule, args->module))
|
||||
{
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
if(args->maxCount != size/sizeof(*out_list))
|
||||
{
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
this->clockMgr->GetFreqList(args->module, out_list, args->maxCount, out_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetKipData() {
|
||||
this->clockMgr->SetKipData();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetKipData() {
|
||||
this->clockMgr->GetKipData();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <nxExt.h>
|
||||
#include <sysclk.h>
|
||||
#include "clock_manager.h"
|
||||
|
||||
class IpcService
|
||||
{
|
||||
public:
|
||||
|
||||
IpcService(ClockManager* clockMgr);
|
||||
virtual ~IpcService();
|
||||
void SetRunning(bool running);
|
||||
static void ProcessThreadFunc(void* arg);
|
||||
static Result ServiceHandlerFunc(void* arg, const IpcServerRequest* r, std::uint8_t* out_data, size_t* out_dataSize);
|
||||
|
||||
Result GetApiVersion(u32* out_version);
|
||||
Result GetVersionString(char* out_buf, size_t bufSize);
|
||||
Result GetCurrentContext(SysClkContext* out_ctx);
|
||||
Result Exit();
|
||||
Result GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count);
|
||||
Result GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles);
|
||||
Result SetProfiles(SysClkIpc_SetProfiles_Args* args);
|
||||
Result SetEnabled(std::uint8_t* enabled);
|
||||
Result SetOverride(SysClkIpc_SetOverride_Args* args);
|
||||
Result GetConfigValues(SysClkConfigValueList* out_configValues);
|
||||
Result SetConfigValues(SysClkConfigValueList* configValues);
|
||||
Result GetFreqList(SysClkIpc_GetFreqList_Args* args, std::uint32_t* out_list, std::size_t size, std::uint32_t* out_count);
|
||||
Result SetKipData();
|
||||
Result GetKipData();
|
||||
bool running;
|
||||
Thread thread;
|
||||
LockableMutex threadMutex;
|
||||
IpcServer server;
|
||||
ClockManager* clockMgr;
|
||||
protected:
|
||||
|
||||
};
|
||||
@@ -12,9 +12,9 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
@@ -24,18 +24,15 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sysclk.h>
|
||||
#include <switch.h>
|
||||
#include <cstdint>
|
||||
|
||||
#define PROCESS_MANAGEMENT_QLAUNCH_TID 0x0100000000001000ULL
|
||||
namespace ipcService {
|
||||
|
||||
class ProcessManagement
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
static void WaitForQLaunch();
|
||||
static std::uint64_t GetCurrentApplicationId();
|
||||
static void Exit();
|
||||
};
|
||||
void Initialize();
|
||||
void Exit();
|
||||
void SetRunning(bool running);
|
||||
|
||||
}
|
||||
255
Source/rewrite-hoc-clk/sysmodule/src/kip.cpp
Normal file
255
Source/rewrite-hoc-clk/sysmodule/src/kip.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "kip.hpp"
|
||||
#include "board/board.hpp"
|
||||
|
||||
namespace kip {
|
||||
|
||||
bool kipAvailable = false;
|
||||
|
||||
void SetKipData()
|
||||
{
|
||||
// TODO: figure out if this REALLY causes issues (i doubt it)
|
||||
// if(board::GetSocType() == SysClkSocType_Mariko) {
|
||||
// if(R_FAILED(I2c_BuckConverter_SetMvOut(&I2c_Mariko_DRAM_VDDQ, config::GetConfigValue(KipConfigValue_marikoEmcVddqVolt) / 1000))) {
|
||||
// fileUtils::LogLine("[clock_manager] Failed set i2c vddq");
|
||||
// notification::writeNotification("Horizon OC\nFailed to write I2C\nwhile setting vddq");
|
||||
// }
|
||||
// }
|
||||
CustomizeTable table;
|
||||
FILE* fp;
|
||||
fp = fopen("sdmc:/atmosphere/kips/hoc.kip", "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
notification::writeNotification("Horizon OC\nKip opening failed");
|
||||
kipAvailable = false;
|
||||
return;
|
||||
} else {
|
||||
kipAvailable = true;
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!cust_read_and_cache("sdmc:/atmosphere/kips/hoc.kip", &table)) {
|
||||
fileUtils::LogLine("[clock_manager] Failed to read KIP file");
|
||||
notification::writeNotification("Horizon OC\nKip read failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, custRev, config::GetConfigValue(KipConfigValue_custRev));
|
||||
// CUST_WRITE_FIELD_BATCH(&table, mtcConf, config::GetConfigValue(KipConfigValue_mtcConf));
|
||||
CUST_WRITE_FIELD_BATCH(&table, hpMode, config::GetConfigValue(KipConfigValue_hpMode));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, commonEmcMemVolt, config::GetConfigValue(KipConfigValue_commonEmcMemVolt));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock, config::GetConfigValue(KipConfigValue_eristaEmcMaxClock));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock1, config::GetConfigValue(KipConfigValue_eristaEmcMaxClock1));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock2, config::GetConfigValue(KipConfigValue_eristaEmcMaxClock2));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoEmcMaxClock, config::GetConfigValue(KipConfigValue_marikoEmcMaxClock));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoEmcVddqVolt, config::GetConfigValue(KipConfigValue_marikoEmcVddqVolt));
|
||||
CUST_WRITE_FIELD_BATCH(&table, emcDvbShift, config::GetConfigValue(KipConfigValue_emcDvbShift));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, t1_tRCD, config::GetConfigValue(KipConfigValue_t1_tRCD));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t2_tRP, config::GetConfigValue(KipConfigValue_t2_tRP));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t3_tRAS, config::GetConfigValue(KipConfigValue_t3_tRAS));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t4_tRRD, config::GetConfigValue(KipConfigValue_t4_tRRD));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t5_tRFC, config::GetConfigValue(KipConfigValue_t5_tRFC));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t6_tRTW, config::GetConfigValue(KipConfigValue_t6_tRTW));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR, config::GetConfigValue(KipConfigValue_t7_tWTR));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t8_tREFI, config::GetConfigValue(KipConfigValue_t8_tREFI));
|
||||
CUST_WRITE_FIELD_BATCH(&table, mem_burst_read_latency, config::GetConfigValue(KipConfigValue_mem_burst_read_latency));
|
||||
CUST_WRITE_FIELD_BATCH(&table, mem_burst_write_latency, config::GetConfigValue(KipConfigValue_mem_burst_write_latency));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuUV, config::GetConfigValue(KipConfigValue_eristaCpuUV));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuVmin, config::GetConfigValue(KipConfigValue_eristaCpuVmin));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuMaxVolt, config::GetConfigValue(KipConfigValue_eristaCpuMaxVolt));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuUnlock, config::GetConfigValue(KipConfigValue_eristaCpuUnlock));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuUVLow, config::GetConfigValue(KipConfigValue_marikoCpuUVLow));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuUVHigh, config::GetConfigValue(KipConfigValue_marikoCpuUVHigh));
|
||||
CUST_WRITE_FIELD_BATCH(&table, tableConf, config::GetConfigValue(KipConfigValue_tableConf));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuLowVmin, config::GetConfigValue(KipConfigValue_marikoCpuLowVmin));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuHighVmin, config::GetConfigValue(KipConfigValue_marikoCpuHighVmin));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuMaxVolt, config::GetConfigValue(KipConfigValue_marikoCpuMaxVolt));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuMaxClock, config::GetConfigValue(KipConfigValue_marikoCpuMaxClock));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuBoostClock, config::GetConfigValue(KipConfigValue_eristaCpuBoostClock));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuBoostClock, config::GetConfigValue(KipConfigValue_marikoCpuBoostClock));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaGpuUV, config::GetConfigValue(KipConfigValue_eristaGpuUV));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaGpuVmin, config::GetConfigValue(KipConfigValue_eristaGpuVmin));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoGpuUV, config::GetConfigValue(KipConfigValue_marikoGpuUV));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoGpuVmin, config::GetConfigValue(KipConfigValue_marikoGpuVmin));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoGpuVmax, config::GetConfigValue(KipConfigValue_marikoGpuVmax));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, commonGpuVoltOffset, config::GetConfigValue(KipConfigValue_commonGpuVoltOffset));
|
||||
CUST_WRITE_FIELD_BATCH(&table, gpuSpeedo, config::GetConfigValue(KipConfigValue_gpuSpeedo));
|
||||
|
||||
for (int i = 0; i < 24; i++) {
|
||||
table.marikoGpuVoltArray[i] = config::GetConfigValue((SysClkConfigValue)(KipConfigValue_g_volt_76800 + i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
table.eristaGpuVoltArray[i] = config::GetConfigValue((SysClkConfigValue)(KipConfigValue_g_volt_e_76800 + i));
|
||||
}
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, t6_tRTW_fine_tune, config::GetConfigValue(KipConfigValue_t6_tRTW_fine_tune));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR_fine_tune, config::GetConfigValue(KipConfigValue_t7_tWTR_fine_tune));
|
||||
|
||||
if (!cust_write_table("sdmc:/atmosphere/kips/hoc.kip", &table)) {
|
||||
fileUtils::LogLine("[clock_manager] Failed to write KIP file");
|
||||
notification::writeNotification("Horizon OC\nKip write failed");
|
||||
}
|
||||
|
||||
SysClkConfigValueList configValues;
|
||||
config::GetConfigValues(&configValues);
|
||||
|
||||
configValues.values[KipCrc32] = (u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum
|
||||
|
||||
if (config::SetConfigValues(&configValues, false)) {
|
||||
fileUtils::LogLine("[clock_manager] Successfully loaded KIP data into config");
|
||||
} else {
|
||||
fileUtils::LogLine("[clock_manager] Warning: Failed to set config values from KIP");
|
||||
notification::writeNotification("Horizon OC\nKip config set failed");
|
||||
}
|
||||
}
|
||||
|
||||
// I know this is very hacky, but the config system in the sysmodule doesn't really support writing
|
||||
|
||||
void GetKipData()
|
||||
{
|
||||
FILE* fp;
|
||||
if (config::Refresh()) {
|
||||
fp = fopen("sdmc:/atmosphere/kips/hoc.kip", "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
notification::writeNotification("Horizon OC\nKip opening failed");
|
||||
kipAvailable = false;
|
||||
return;
|
||||
} else {
|
||||
kipAvailable = true;
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
SysClkConfigValueList configValues;
|
||||
config::GetConfigValues(&configValues);
|
||||
|
||||
CustomizeTable table;
|
||||
|
||||
if (!cust_read_and_cache("sdmc:/atmosphere/kips/hoc.kip", &table)) {
|
||||
fileUtils::LogLine("[clock_manager] Failed to read KIP file for GetKipData");
|
||||
notification::writeNotification("Horizon OC\nKip read failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip") != config::GetConfigValue(KipCrc32) && !config::GetConfigValue(HocClkConfigValue_IsFirstLoad)) {
|
||||
SetKipData();
|
||||
notification::writeNotification("Horizon OC\nKIP has been updated");
|
||||
notification::writeNotification("Horizon OC\nPlease reboot your console");
|
||||
notification::writeNotification("Horizon OC\nto complete the update");
|
||||
return;
|
||||
}
|
||||
if (config::GetConfigValue(HocClkConfigValue_IsFirstLoad) == true) {
|
||||
configValues.values[HocClkConfigValue_IsFirstLoad] = (u64)false;
|
||||
notification::writeNotification("Horizon OC has been installed");
|
||||
}
|
||||
|
||||
configValues.values[KipCrc32] = (u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum
|
||||
// configValues.values[KipConfigValue_mtcConf] = cust_get_mtc_conf(&table);
|
||||
configValues.values[KipConfigValue_hpMode] = cust_get_hp_mode(&table);
|
||||
|
||||
configValues.values[KipConfigValue_commonEmcMemVolt] = cust_get_common_emc_volt(&table);
|
||||
configValues.values[KipConfigValue_eristaEmcMaxClock] = cust_get_erista_emc_max(&table);
|
||||
configValues.values[KipConfigValue_eristaEmcMaxClock1] = cust_get_erista_emc_max1(&table);
|
||||
configValues.values[KipConfigValue_eristaEmcMaxClock2] = cust_get_erista_emc_max2(&table);
|
||||
configValues.values[KipConfigValue_marikoEmcMaxClock] = cust_get_mariko_emc_max(&table);
|
||||
configValues.values[KipConfigValue_marikoEmcVddqVolt] = cust_get_mariko_emc_vddq(&table);
|
||||
configValues.values[KipConfigValue_emcDvbShift] = cust_get_emc_dvb_shift(&table);
|
||||
|
||||
configValues.values[KipConfigValue_t1_tRCD] = cust_get_tRCD(&table);
|
||||
configValues.values[KipConfigValue_t2_tRP] = cust_get_tRP(&table);
|
||||
configValues.values[KipConfigValue_t3_tRAS] = cust_get_tRAS(&table);
|
||||
configValues.values[KipConfigValue_t4_tRRD] = cust_get_tRRD(&table);
|
||||
configValues.values[KipConfigValue_t5_tRFC] = cust_get_tRFC(&table);
|
||||
configValues.values[KipConfigValue_t6_tRTW] = cust_get_tRTW(&table);
|
||||
configValues.values[KipConfigValue_t7_tWTR] = cust_get_tWTR(&table);
|
||||
configValues.values[KipConfigValue_t8_tREFI] = cust_get_tREFI(&table);
|
||||
configValues.values[KipConfigValue_mem_burst_read_latency] = cust_get_burst_read_lat(&table);
|
||||
configValues.values[KipConfigValue_mem_burst_write_latency] = cust_get_burst_write_lat(&table);
|
||||
|
||||
configValues.values[KipConfigValue_eristaCpuUV] = cust_get_erista_cpu_uv(&table);
|
||||
configValues.values[KipConfigValue_eristaCpuVmin] = cust_get_eristaCpuVmin(&table);
|
||||
configValues.values[KipConfigValue_eristaCpuMaxVolt] = cust_get_erista_cpu_max_volt(&table);
|
||||
configValues.values[KipConfigValue_eristaCpuUnlock] = cust_get_eristaCpuUnlock(&table);
|
||||
|
||||
configValues.values[KipConfigValue_marikoCpuUVLow] = cust_get_mariko_cpu_uv_low(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuUVHigh] = cust_get_mariko_cpu_uv_high(&table);
|
||||
configValues.values[KipConfigValue_tableConf] = cust_get_table_conf(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuLowVmin] = cust_get_mariko_cpu_low_vmin(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuHighVmin] = cust_get_mariko_cpu_high_vmin(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuMaxVolt] = cust_get_mariko_cpu_max_volt(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuMaxClock] = cust_get_marikoCpuMaxClock(&table);
|
||||
configValues.values[KipConfigValue_eristaCpuBoostClock] = cust_get_erista_cpu_boost(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuBoostClock] = cust_get_mariko_cpu_boost(&table);
|
||||
|
||||
configValues.values[KipConfigValue_eristaGpuUV] = cust_get_erista_gpu_uv(&table);
|
||||
configValues.values[KipConfigValue_eristaGpuVmin] = cust_get_erista_gpu_vmin(&table);
|
||||
configValues.values[KipConfigValue_marikoGpuUV] = cust_get_mariko_gpu_uv(&table);
|
||||
configValues.values[KipConfigValue_marikoGpuVmin] = cust_get_mariko_gpu_vmin(&table);
|
||||
configValues.values[KipConfigValue_marikoGpuVmax] = cust_get_mariko_gpu_vmax(&table);
|
||||
configValues.values[KipConfigValue_commonGpuVoltOffset] = cust_get_common_gpu_offset(&table);
|
||||
configValues.values[KipConfigValue_gpuSpeedo] = board::GetFuseData()->gpuSpeedo; // cust_get_gpu_speedo(&table);
|
||||
|
||||
for (int i = 0; i < 24; i++) {
|
||||
configValues.values[KipConfigValue_g_volt_76800 + i] = cust_get_mariko_gpu_volt(&table, i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
configValues.values[KipConfigValue_g_volt_e_76800 + i] = cust_get_erista_gpu_volt(&table, i);
|
||||
}
|
||||
|
||||
configValues.values[KipConfigValue_t7_tWTR_fine_tune] = cust_get_tWTR_fine_tune(&table);
|
||||
configValues.values[KipConfigValue_t6_tRTW_fine_tune] = cust_get_tRTW_fine_tune(&table);
|
||||
|
||||
// if(cust_get_cust_rev(&table) == KIP_CUST_REV)
|
||||
// return;
|
||||
|
||||
if (sizeof(SysClkConfigValueList) <= sizeof(configValues)) {
|
||||
if (config::SetConfigValues(&configValues, false)) {
|
||||
fileUtils::LogLine("[clock_manager] Successfully loaded KIP data into config");
|
||||
} else {
|
||||
fileUtils::LogLine("[clock_manager] Warning: Failed to set config values from KIP");
|
||||
notification::writeNotification("Horizon OC\nKip config set failed");
|
||||
}
|
||||
} else {
|
||||
fileUtils::LogLine("[clock_manager] Error: Config value list buffer size mismatch");
|
||||
notification::writeNotification("Horizon OC\nConfig Buffer Mismatch");
|
||||
}
|
||||
} else {
|
||||
fileUtils::LogLine("[clock_manager] Config refresh error in GetKipData!");
|
||||
notification::writeNotification("Horizon OC\nConfig refresh failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,450 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct {
|
||||
u8 cust[4];
|
||||
u32 custRev;
|
||||
u32 placeholder;
|
||||
u32 hpMode;
|
||||
u32 commonEmcMemVolt;
|
||||
u32 eristaEmcMaxClock;
|
||||
u32 eristaEmcMaxClock1;
|
||||
u32 eristaEmcMaxClock2;
|
||||
u32 marikoEmcMaxClock;
|
||||
u32 marikoEmcVddqVolt;
|
||||
u32 emcDvbShift;
|
||||
// advanced config
|
||||
u32 t1_tRCD;
|
||||
u32 t2_tRP;
|
||||
u32 t3_tRAS;
|
||||
u32 t4_tRRD;
|
||||
u32 t5_tRFC;
|
||||
u32 t6_tRTW;
|
||||
u32 t7_tWTR;
|
||||
u32 t8_tREFI;
|
||||
|
||||
u32 mem_burst_read_latency;
|
||||
u32 mem_burst_write_latency;
|
||||
|
||||
u32 eristaCpuUV;
|
||||
u32 eristaCpuVmin;
|
||||
u32 eristaCpuMaxVolt;
|
||||
u32 eristaCpuUnlock;
|
||||
|
||||
u32 marikoCpuUVLow;
|
||||
u32 marikoCpuUVHigh;
|
||||
u32 tableConf;
|
||||
u32 marikoCpuLowVmin;
|
||||
u32 marikoCpuHighVmin;
|
||||
u32 marikoCpuMaxVolt;
|
||||
u32 marikoCpuMaxClock;
|
||||
|
||||
u32 eristaCpuBoostClock;
|
||||
u32 marikoCpuBoostClock;
|
||||
|
||||
u32 eristaGpuUV;
|
||||
u32 eristaGpuVmin;
|
||||
|
||||
u32 marikoGpuUV;
|
||||
u32 marikoGpuVmin;
|
||||
u32 marikoGpuVmax;
|
||||
|
||||
u32 commonGpuVoltOffset;
|
||||
|
||||
u32 gpuSpeedo;
|
||||
|
||||
u32 eristaGpuVoltArray[27];
|
||||
u32 marikoGpuVoltArray[24];
|
||||
|
||||
u32 t6_tRTW_fine_tune;
|
||||
u32 t7_tWTR_fine_tune;
|
||||
|
||||
u32 reserved[60];
|
||||
} CustomizeTable;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define CUST_MAGIC "CUST"
|
||||
#define CUST_MAGIC_LEN 4
|
||||
|
||||
typedef struct {
|
||||
FILE* file;
|
||||
long offset;
|
||||
CustomizeTable cached_table;
|
||||
bool has_cache;
|
||||
} CustHandle;
|
||||
|
||||
static inline bool cust_find_offset(FILE* f, long* out_offset) {
|
||||
u8 buf[512];
|
||||
long pos = 0;
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
while (1) {
|
||||
size_t r = fread(buf, 1, sizeof(buf), f);
|
||||
if (r < CUST_MAGIC_LEN) break;
|
||||
|
||||
for (size_t i = 0; i <= r - CUST_MAGIC_LEN; i++) {
|
||||
if (memcmp(&buf[i], CUST_MAGIC, CUST_MAGIC_LEN) == 0) {
|
||||
*out_offset = pos + (long)i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
pos += (long)(r - (CUST_MAGIC_LEN - 1));
|
||||
fseek(f, pos, SEEK_SET);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cust_read_table(const char* path, CustomizeTable* out) {
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) return false;
|
||||
|
||||
long off;
|
||||
if (!cust_find_offset(f, &off)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
|
||||
if (off + (long)sizeof(CustomizeTable) > size) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, off, SEEK_SET);
|
||||
bool ok = fread(out, 1, sizeof(CustomizeTable), f) == sizeof(CustomizeTable);
|
||||
fclose(f);
|
||||
|
||||
return ok && memcmp(out->cust, CUST_MAGIC, CUST_MAGIC_LEN) == 0;
|
||||
}
|
||||
|
||||
static inline bool cust_write_table(const char* path, const CustomizeTable* in) {
|
||||
FILE* f = fopen(path, "r+b");
|
||||
if (!f) return false;
|
||||
|
||||
long off;
|
||||
if (!cust_find_offset(f, &off)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
|
||||
if (off + (long)sizeof(CustomizeTable) > size) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, off, SEEK_SET);
|
||||
bool ok = fwrite(in, 1, sizeof(CustomizeTable), f) == sizeof(CustomizeTable);
|
||||
fflush(f);
|
||||
fclose(f);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
static inline bool cust_read_and_cache(const char* path, CustomizeTable* out) {
|
||||
return cust_read_table(path, out);
|
||||
}
|
||||
|
||||
#define CUST_WRITE_FIELD_BATCH(table, field, val) \
|
||||
do { \
|
||||
(table)->field = (val); \
|
||||
} while (0)
|
||||
|
||||
#define CUST_WRITE_FIELD(path, field, val) \
|
||||
do { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(path, &t)) return false; \
|
||||
t.field = (val); \
|
||||
return cust_write_table(path, &t); \
|
||||
} while (0)
|
||||
|
||||
static inline bool cust_set_cust_rev(const char* p, u32 v) { CUST_WRITE_FIELD(p, custRev, v); }
|
||||
// static inline bool cust_set_mtc_conf(const char* p, u32 v) { CUST_WRITE_FIELD(p, mtcConf, v); }
|
||||
static inline bool cust_set_hp_mode(const char* p, u32 v) { CUST_WRITE_FIELD(p, hpMode, v); }
|
||||
|
||||
static inline bool cust_set_common_emc_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, commonEmcMemVolt, v); }
|
||||
static inline bool cust_set_erista_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock, v); }
|
||||
static inline bool cust_set_erista_emc_max1(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock1, v); }
|
||||
static inline bool cust_set_erista_emc_max2(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock2, v); }
|
||||
static inline bool cust_set_mariko_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcMaxClock, v); }
|
||||
static inline bool cust_set_mariko_emc_vddq(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcVddqVolt, v); }
|
||||
static inline bool cust_set_emc_dvb_shift(const char* p, u32 v) { CUST_WRITE_FIELD(p, emcDvbShift, v); }
|
||||
|
||||
static inline bool cust_set_tRCD(const char* p, u32 v) { CUST_WRITE_FIELD(p, t1_tRCD, v); }
|
||||
static inline bool cust_set_tRP(const char* p, u32 v) { CUST_WRITE_FIELD(p, t2_tRP, v); }
|
||||
static inline bool cust_set_tRAS(const char* p, u32 v) { CUST_WRITE_FIELD(p, t3_tRAS, v); }
|
||||
static inline bool cust_set_tRRD(const char* p, u32 v) { CUST_WRITE_FIELD(p, t4_tRRD, v); }
|
||||
static inline bool cust_set_tRFC(const char* p, u32 v) { CUST_WRITE_FIELD(p, t5_tRFC, v); }
|
||||
static inline bool cust_set_tRTW(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW, v); }
|
||||
static inline bool cust_set_tWTR(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR, v); }
|
||||
static inline bool cust_set_tREFI(const char* p, u32 v) { CUST_WRITE_FIELD(p, t8_tREFI, v); }
|
||||
static inline bool cust_set_tRTW_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW_fine_tune, v); }
|
||||
static inline bool cust_set_tWTR_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR_fine_tune, v); }
|
||||
static inline bool cust_set_burst_read_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_read_latency, v); }
|
||||
static inline bool cust_set_burst_write_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_write_latency, v); }
|
||||
|
||||
static inline bool cust_set_erista_cpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuUV, v); }
|
||||
static inline bool cust_set_eristaCpuVmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuVmin, v); }
|
||||
static inline bool cust_set_erista_cpu_max_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuMaxVolt, v); }
|
||||
static inline bool cust_set_eristaCpuUnlock(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuUnlock, v); }
|
||||
|
||||
static inline bool cust_set_mariko_cpu_uv_low(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuUVLow, v); }
|
||||
static inline bool cust_set_mariko_cpu_uv_high(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuUVHigh, v); }
|
||||
static inline bool cust_set_mariko_cpu_low_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuLowVmin, v); }
|
||||
static inline bool cust_set_mariko_cpu_high_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuHighVmin, v); }
|
||||
static inline bool cust_set_mariko_cpu_max_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuMaxVolt, v); }
|
||||
static inline bool cust_set_erista_cpu_boost(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuBoostClock, v); }
|
||||
static inline bool cust_set_mariko_cpu_boost(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuBoostClock, v); }
|
||||
|
||||
static inline bool cust_set_erista_gpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaGpuUV, v); }
|
||||
static inline bool cust_set_erista_gpu_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaGpuVmin, v); }
|
||||
static inline bool cust_set_mariko_gpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuUV, v); }
|
||||
static inline bool cust_set_mariko_gpu_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuVmin, v); }
|
||||
static inline bool cust_set_mariko_gpu_vmax(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuVmax, v); }
|
||||
static inline bool cust_set_common_gpu_offset(const char* p, u32 v) { CUST_WRITE_FIELD(p, commonGpuVoltOffset, v); }
|
||||
static inline bool cust_set_gpu_speedo(const char* p, u32 v) { CUST_WRITE_FIELD(p, gpuSpeedo, v); }
|
||||
static inline bool cust_set_marikoCpuMaxClock(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuMaxClock, v); }
|
||||
|
||||
/* GPU VOLT ARRAY HELPERS */
|
||||
static inline bool cust_set_erista_gpu_volt(const char* p, int idx, u32 v) {
|
||||
if (idx < 0 || idx >= 27) return false;
|
||||
CustomizeTable t;
|
||||
if (!cust_read_table(p, &t)) return false;
|
||||
t.eristaGpuVoltArray[idx] = v;
|
||||
return cust_write_table(p, &t);
|
||||
}
|
||||
|
||||
static inline bool cust_set_mariko_gpu_volt(const char* p, int idx, u32 v) {
|
||||
if (idx < 0 || idx >= 24) return false;
|
||||
CustomizeTable t;
|
||||
if (!cust_read_table(p, &t)) return false;
|
||||
t.marikoGpuVoltArray[idx] = v;
|
||||
return cust_write_table(p, &t);
|
||||
}
|
||||
|
||||
static inline u32 cust_get_field(const CustomizeTable* t, u32 offset) {
|
||||
if (!t) return 0;
|
||||
return *(u32*)((u8*)t + offset);
|
||||
}
|
||||
|
||||
#define CUST_GET_FIELD(table, field) ((table) ? (table)->field : 0)
|
||||
|
||||
static inline u32 cust_get_cust_rev(const CustomizeTable* t) { return CUST_GET_FIELD(t, custRev); }
|
||||
// static inline u32 cust_get_mtc_conf(const CustomizeTable* t) { return CUST_GET_FIELD(t, mtcConf); }
|
||||
static inline u32 cust_get_hp_mode(const CustomizeTable* t) { return CUST_GET_FIELD(t, hpMode); }
|
||||
|
||||
static inline u32 cust_get_common_emc_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, commonEmcMemVolt); }
|
||||
static inline u32 cust_get_erista_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock); }
|
||||
static inline u32 cust_get_erista_emc_max1(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock1); }
|
||||
static inline u32 cust_get_erista_emc_max2(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock2); }
|
||||
static inline u32 cust_get_mariko_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcMaxClock); }
|
||||
static inline u32 cust_get_mariko_emc_vddq(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcVddqVolt); }
|
||||
static inline u32 cust_get_emc_dvb_shift(const CustomizeTable* t) { return CUST_GET_FIELD(t, emcDvbShift); }
|
||||
|
||||
static inline u32 cust_get_tRCD(const CustomizeTable* t) { return CUST_GET_FIELD(t, t1_tRCD); }
|
||||
static inline u32 cust_get_tRP(const CustomizeTable* t) { return CUST_GET_FIELD(t, t2_tRP); }
|
||||
static inline u32 cust_get_tRAS(const CustomizeTable* t) { return CUST_GET_FIELD(t, t3_tRAS); }
|
||||
static inline u32 cust_get_tRRD(const CustomizeTable* t) { return CUST_GET_FIELD(t, t4_tRRD); }
|
||||
static inline u32 cust_get_tRFC(const CustomizeTable* t) { return CUST_GET_FIELD(t, t5_tRFC); }
|
||||
static inline u32 cust_get_tRTW(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW); }
|
||||
static inline u32 cust_get_tWTR(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR); }
|
||||
static inline u32 cust_get_tREFI(const CustomizeTable* t) { return CUST_GET_FIELD(t, t8_tREFI); }
|
||||
static inline u32 cust_get_tRTW_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW_fine_tune); }
|
||||
static inline u32 cust_get_tWTR_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR_fine_tune); }
|
||||
static inline u32 cust_get_burst_read_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_read_latency); }
|
||||
static inline u32 cust_get_burst_write_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_write_latency); }
|
||||
|
||||
static inline u32 cust_get_erista_cpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuUV); }
|
||||
static inline u32 cust_get_eristaCpuVmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuVmin); }
|
||||
static inline u32 cust_get_erista_cpu_max_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuMaxVolt); }
|
||||
static inline u32 cust_get_eristaCpuUnlock(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuUnlock); }
|
||||
|
||||
static inline u32 cust_get_mariko_cpu_uv_low(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuUVLow); }
|
||||
static inline u32 cust_get_mariko_cpu_uv_high(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuUVHigh); }
|
||||
static inline u32 cust_get_mariko_cpu_low_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuLowVmin); }
|
||||
static inline u32 cust_get_mariko_cpu_high_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuHighVmin); }
|
||||
static inline u32 cust_get_mariko_cpu_max_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuMaxVolt); }
|
||||
static inline u32 cust_get_erista_cpu_boost(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuBoostClock); }
|
||||
static inline u32 cust_get_mariko_cpu_boost(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuBoostClock); }
|
||||
static inline u32 cust_get_table_conf(const CustomizeTable* t) { return CUST_GET_FIELD(t, tableConf); }
|
||||
|
||||
static inline u32 cust_get_erista_gpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaGpuUV); }
|
||||
static inline u32 cust_get_erista_gpu_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaGpuVmin); }
|
||||
static inline u32 cust_get_mariko_gpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuUV); }
|
||||
static inline u32 cust_get_mariko_gpu_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuVmin); }
|
||||
static inline u32 cust_get_mariko_gpu_vmax(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuVmax); }
|
||||
static inline u32 cust_get_common_gpu_offset(const CustomizeTable* t) { return CUST_GET_FIELD(t, commonGpuVoltOffset); }
|
||||
static inline u32 cust_get_gpu_speedo(const CustomizeTable* t) { return CUST_GET_FIELD(t, gpuSpeedo); }
|
||||
static inline u32 cust_get_marikoCpuMaxClock(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuMaxClock); }
|
||||
|
||||
static inline u32 cust_get_erista_gpu_volt(const CustomizeTable* t, int idx) {
|
||||
if (!t || idx < 0 || idx >= 27) return 0;
|
||||
return t->eristaGpuVoltArray[idx];
|
||||
}
|
||||
|
||||
static inline u32 cust_get_mariko_gpu_volt(const CustomizeTable* t, int idx) {
|
||||
if (!t || idx < 0 || idx >= 24) return 0;
|
||||
return t->marikoGpuVoltArray[idx];
|
||||
}
|
||||
|
||||
#define DECL_ERISTA_GPU_VOLT_HELPER(freq, idx) \
|
||||
static inline bool cust_set_erista_gpu_volt_##freq( \
|
||||
const char* p, u32 v) { \
|
||||
return cust_set_erista_gpu_volt(p, idx, v); \
|
||||
}
|
||||
|
||||
#define DECL_MARIKO_GPU_VOLT_HELPER(freq, idx) \
|
||||
static inline bool cust_set_mariko_gpu_volt_##freq( \
|
||||
const char* p, u32 v) { \
|
||||
return cust_set_mariko_gpu_volt(p, idx, v); \
|
||||
}
|
||||
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(76800, 0)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(115200, 1)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(153600, 2)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(192000, 3)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(230400, 4)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(268800, 5)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(307200, 6)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(345600, 7)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(384000, 8)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(422400, 9)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(460800, 10)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(499200, 11)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(537600, 12)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(576000, 13)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(614400, 14)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(652800, 15)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(691200, 16)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(729600, 17)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(768000, 18)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(806400, 19)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(844800, 20)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(883200, 21)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(921600, 22)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(960000, 23)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(998400, 24)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(1036800, 25)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(1075200, 26)
|
||||
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(76800, 0)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(153600, 1)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(230400, 2)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(307200, 3)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(384000, 4)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(460800, 5)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(537600, 6)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(614400, 7)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(691200, 8)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(768000, 9)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(844800, 10)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(921600, 11)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(998400, 12)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1075200, 13)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1152000, 14)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1228800, 15)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1267200, 16)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1305600, 17)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1344000, 18)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1382400, 19)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1420800, 20)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1459200, 21)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1497600, 22)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1536000, 23)
|
||||
|
||||
|
||||
#define DECL_ERISTA_GPU_VOLT_GET(freq, idx) \
|
||||
static inline u32 cust_get_erista_gpu_volt_##freq##_val(const char* p) { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(p, &t)) return 0; \
|
||||
return cust_get_erista_gpu_volt(&t, idx); \
|
||||
}
|
||||
#define DECL_MARIKO_GPU_VOLT_GET(freq, idx) \
|
||||
static inline u32 cust_get_mariko_gpu_volt_##freq##_val(const char* p) { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(p, &t)) return 0; \
|
||||
return cust_get_mariko_gpu_volt(&t, idx); \
|
||||
}
|
||||
|
||||
DECL_ERISTA_GPU_VOLT_GET(76800, 0)
|
||||
DECL_ERISTA_GPU_VOLT_GET(115200, 1)
|
||||
DECL_ERISTA_GPU_VOLT_GET(153600, 2)
|
||||
DECL_ERISTA_GPU_VOLT_GET(192000, 3)
|
||||
DECL_ERISTA_GPU_VOLT_GET(230400, 4)
|
||||
DECL_ERISTA_GPU_VOLT_GET(268800, 5)
|
||||
DECL_ERISTA_GPU_VOLT_GET(307200, 6)
|
||||
DECL_ERISTA_GPU_VOLT_GET(345600, 7)
|
||||
DECL_ERISTA_GPU_VOLT_GET(384000, 8)
|
||||
DECL_ERISTA_GPU_VOLT_GET(422400, 9)
|
||||
DECL_ERISTA_GPU_VOLT_GET(460800, 10)
|
||||
DECL_ERISTA_GPU_VOLT_GET(499200, 11)
|
||||
DECL_ERISTA_GPU_VOLT_GET(537600, 12)
|
||||
DECL_ERISTA_GPU_VOLT_GET(576000, 13)
|
||||
DECL_ERISTA_GPU_VOLT_GET(614400, 14)
|
||||
DECL_ERISTA_GPU_VOLT_GET(652800, 15)
|
||||
DECL_ERISTA_GPU_VOLT_GET(691200, 16)
|
||||
DECL_ERISTA_GPU_VOLT_GET(729600, 17)
|
||||
DECL_ERISTA_GPU_VOLT_GET(768000, 18)
|
||||
DECL_ERISTA_GPU_VOLT_GET(806400, 19)
|
||||
DECL_ERISTA_GPU_VOLT_GET(844800, 20)
|
||||
DECL_ERISTA_GPU_VOLT_GET(883200, 21)
|
||||
DECL_ERISTA_GPU_VOLT_GET(921600, 22)
|
||||
DECL_ERISTA_GPU_VOLT_GET(960000, 23)
|
||||
DECL_ERISTA_GPU_VOLT_GET(998400, 24)
|
||||
DECL_ERISTA_GPU_VOLT_GET(1036800, 25)
|
||||
DECL_ERISTA_GPU_VOLT_GET(1075200, 26)
|
||||
|
||||
DECL_MARIKO_GPU_VOLT_GET(76800, 0)
|
||||
DECL_MARIKO_GPU_VOLT_GET(153600, 1)
|
||||
DECL_MARIKO_GPU_VOLT_GET(230400, 2)
|
||||
DECL_MARIKO_GPU_VOLT_GET(307200, 3)
|
||||
DECL_MARIKO_GPU_VOLT_GET(384000, 4)
|
||||
DECL_MARIKO_GPU_VOLT_GET(460800, 5)
|
||||
DECL_MARIKO_GPU_VOLT_GET(537600, 6)
|
||||
DECL_MARIKO_GPU_VOLT_GET(614400, 7)
|
||||
DECL_MARIKO_GPU_VOLT_GET(691200, 8)
|
||||
DECL_MARIKO_GPU_VOLT_GET(768000, 9)
|
||||
DECL_MARIKO_GPU_VOLT_GET(844800, 10)
|
||||
DECL_MARIKO_GPU_VOLT_GET(921600, 11)
|
||||
DECL_MARIKO_GPU_VOLT_GET(998400, 12)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1075200, 13)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1152000, 14)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1228800, 15)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1267200, 16)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1305600, 17)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1344000, 18)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1382400, 19)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1420800, 20)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1459200, 21)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1497600, 22)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1536000, 23)
|
||||
467
Source/rewrite-hoc-clk/sysmodule/src/kip.hpp
Normal file
467
Source/rewrite-hoc-clk/sysmodule/src/kip.hpp
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "config.hpp"
|
||||
#include "file_utils.hpp"
|
||||
#include <notification.h>
|
||||
#include <crc32.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
namespace kip {
|
||||
extern bool kipAvailable;
|
||||
|
||||
typedef struct {
|
||||
u8 cust[4];
|
||||
u32 custRev;
|
||||
u32 placeholder;
|
||||
u32 hpMode;
|
||||
u32 commonEmcMemVolt;
|
||||
u32 eristaEmcMaxClock;
|
||||
u32 eristaEmcMaxClock1;
|
||||
u32 eristaEmcMaxClock2;
|
||||
u32 marikoEmcMaxClock;
|
||||
u32 marikoEmcVddqVolt;
|
||||
u32 emcDvbShift;
|
||||
// advanced config
|
||||
u32 t1_tRCD;
|
||||
u32 t2_tRP;
|
||||
u32 t3_tRAS;
|
||||
u32 t4_tRRD;
|
||||
u32 t5_tRFC;
|
||||
u32 t6_tRTW;
|
||||
u32 t7_tWTR;
|
||||
u32 t8_tREFI;
|
||||
|
||||
u32 mem_burst_read_latency;
|
||||
u32 mem_burst_write_latency;
|
||||
|
||||
u32 eristaCpuUV;
|
||||
u32 eristaCpuVmin;
|
||||
u32 eristaCpuMaxVolt;
|
||||
u32 eristaCpuUnlock;
|
||||
|
||||
u32 marikoCpuUVLow;
|
||||
u32 marikoCpuUVHigh;
|
||||
u32 tableConf;
|
||||
u32 marikoCpuLowVmin;
|
||||
u32 marikoCpuHighVmin;
|
||||
u32 marikoCpuMaxVolt;
|
||||
u32 marikoCpuMaxClock;
|
||||
|
||||
u32 eristaCpuBoostClock;
|
||||
u32 marikoCpuBoostClock;
|
||||
|
||||
u32 eristaGpuUV;
|
||||
u32 eristaGpuVmin;
|
||||
|
||||
u32 marikoGpuUV;
|
||||
u32 marikoGpuVmin;
|
||||
u32 marikoGpuVmax;
|
||||
|
||||
u32 commonGpuVoltOffset;
|
||||
|
||||
u32 gpuSpeedo;
|
||||
|
||||
u32 eristaGpuVoltArray[27];
|
||||
u32 marikoGpuVoltArray[24];
|
||||
|
||||
u32 t6_tRTW_fine_tune;
|
||||
u32 t7_tWTR_fine_tune;
|
||||
|
||||
u32 reserved[60];
|
||||
} CustomizeTable;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define CUST_MAGIC "CUST"
|
||||
#define CUST_MAGIC_LEN 4
|
||||
|
||||
typedef struct {
|
||||
FILE* file;
|
||||
long offset;
|
||||
CustomizeTable cached_table;
|
||||
bool has_cache;
|
||||
} CustHandle;
|
||||
|
||||
static inline bool cust_find_offset(FILE* f, long* out_offset) {
|
||||
u8 buf[512];
|
||||
long pos = 0;
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
while (1) {
|
||||
size_t r = fread(buf, 1, sizeof(buf), f);
|
||||
if (r < CUST_MAGIC_LEN) break;
|
||||
|
||||
for (size_t i = 0; i <= r - CUST_MAGIC_LEN; i++) {
|
||||
if (memcmp(&buf[i], CUST_MAGIC, CUST_MAGIC_LEN) == 0) {
|
||||
*out_offset = pos + (long)i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
pos += (long)(r - (CUST_MAGIC_LEN - 1));
|
||||
fseek(f, pos, SEEK_SET);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cust_read_table(const char* path, CustomizeTable* out) {
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) return false;
|
||||
|
||||
long off;
|
||||
if (!cust_find_offset(f, &off)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
|
||||
if (off + (long)sizeof(CustomizeTable) > size) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, off, SEEK_SET);
|
||||
bool ok = fread(out, 1, sizeof(CustomizeTable), f) == sizeof(CustomizeTable);
|
||||
fclose(f);
|
||||
|
||||
return ok && memcmp(out->cust, CUST_MAGIC, CUST_MAGIC_LEN) == 0;
|
||||
}
|
||||
|
||||
static inline bool cust_write_table(const char* path, const CustomizeTable* in) {
|
||||
FILE* f = fopen(path, "r+b");
|
||||
if (!f) return false;
|
||||
|
||||
long off;
|
||||
if (!cust_find_offset(f, &off)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
|
||||
if (off + (long)sizeof(CustomizeTable) > size) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, off, SEEK_SET);
|
||||
bool ok = fwrite(in, 1, sizeof(CustomizeTable), f) == sizeof(CustomizeTable);
|
||||
fflush(f);
|
||||
fclose(f);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static inline bool cust_read_and_cache(const char* path, CustomizeTable* out) {
|
||||
return cust_read_table(path, out);
|
||||
}
|
||||
|
||||
#define CUST_WRITE_FIELD_BATCH(table, field, val) \
|
||||
do { \
|
||||
(table)->field = (val); \
|
||||
} while (0)
|
||||
|
||||
#define CUST_WRITE_FIELD(path, field, val) \
|
||||
do { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(path, &t)) return false; \
|
||||
t.field = (val); \
|
||||
return cust_write_table(path, &t); \
|
||||
} while (0)
|
||||
|
||||
static inline bool cust_set_cust_rev(const char* p, u32 v) { CUST_WRITE_FIELD(p, custRev, v); }
|
||||
// static inline bool cust_set_mtc_conf(const char* p, u32 v) { CUST_WRITE_FIELD(p, mtcConf, v); }
|
||||
static inline bool cust_set_hp_mode(const char* p, u32 v) { CUST_WRITE_FIELD(p, hpMode, v); }
|
||||
|
||||
static inline bool cust_set_common_emc_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, commonEmcMemVolt, v); }
|
||||
static inline bool cust_set_erista_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock, v); }
|
||||
static inline bool cust_set_erista_emc_max1(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock1, v); }
|
||||
static inline bool cust_set_erista_emc_max2(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock2, v); }
|
||||
static inline bool cust_set_mariko_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcMaxClock, v); }
|
||||
static inline bool cust_set_mariko_emc_vddq(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcVddqVolt, v); }
|
||||
static inline bool cust_set_emc_dvb_shift(const char* p, u32 v) { CUST_WRITE_FIELD(p, emcDvbShift, v); }
|
||||
|
||||
static inline bool cust_set_tRCD(const char* p, u32 v) { CUST_WRITE_FIELD(p, t1_tRCD, v); }
|
||||
static inline bool cust_set_tRP(const char* p, u32 v) { CUST_WRITE_FIELD(p, t2_tRP, v); }
|
||||
static inline bool cust_set_tRAS(const char* p, u32 v) { CUST_WRITE_FIELD(p, t3_tRAS, v); }
|
||||
static inline bool cust_set_tRRD(const char* p, u32 v) { CUST_WRITE_FIELD(p, t4_tRRD, v); }
|
||||
static inline bool cust_set_tRFC(const char* p, u32 v) { CUST_WRITE_FIELD(p, t5_tRFC, v); }
|
||||
static inline bool cust_set_tRTW(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW, v); }
|
||||
static inline bool cust_set_tWTR(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR, v); }
|
||||
static inline bool cust_set_tREFI(const char* p, u32 v) { CUST_WRITE_FIELD(p, t8_tREFI, v); }
|
||||
static inline bool cust_set_tRTW_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW_fine_tune, v); }
|
||||
static inline bool cust_set_tWTR_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR_fine_tune, v); }
|
||||
static inline bool cust_set_burst_read_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_read_latency, v); }
|
||||
static inline bool cust_set_burst_write_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_write_latency, v); }
|
||||
|
||||
static inline bool cust_set_erista_cpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuUV, v); }
|
||||
static inline bool cust_set_eristaCpuVmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuVmin, v); }
|
||||
static inline bool cust_set_erista_cpu_max_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuMaxVolt, v); }
|
||||
static inline bool cust_set_eristaCpuUnlock(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuUnlock, v); }
|
||||
|
||||
static inline bool cust_set_mariko_cpu_uv_low(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuUVLow, v); }
|
||||
static inline bool cust_set_mariko_cpu_uv_high(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuUVHigh, v); }
|
||||
static inline bool cust_set_mariko_cpu_low_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuLowVmin, v); }
|
||||
static inline bool cust_set_mariko_cpu_high_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuHighVmin, v); }
|
||||
static inline bool cust_set_mariko_cpu_max_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuMaxVolt, v); }
|
||||
static inline bool cust_set_erista_cpu_boost(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuBoostClock, v); }
|
||||
static inline bool cust_set_mariko_cpu_boost(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuBoostClock, v); }
|
||||
|
||||
static inline bool cust_set_erista_gpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaGpuUV, v); }
|
||||
static inline bool cust_set_erista_gpu_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaGpuVmin, v); }
|
||||
static inline bool cust_set_mariko_gpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuUV, v); }
|
||||
static inline bool cust_set_mariko_gpu_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuVmin, v); }
|
||||
static inline bool cust_set_mariko_gpu_vmax(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuVmax, v); }
|
||||
static inline bool cust_set_common_gpu_offset(const char* p, u32 v) { CUST_WRITE_FIELD(p, commonGpuVoltOffset, v); }
|
||||
static inline bool cust_set_gpu_speedo(const char* p, u32 v) { CUST_WRITE_FIELD(p, gpuSpeedo, v); }
|
||||
static inline bool cust_set_marikoCpuMaxClock(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuMaxClock, v); }
|
||||
|
||||
/* GPU VOLT ARRAY HELPERS */
|
||||
static inline bool cust_set_erista_gpu_volt(const char* p, int idx, u32 v) {
|
||||
if (idx < 0 || idx >= 27) return false;
|
||||
CustomizeTable t;
|
||||
if (!cust_read_table(p, &t)) return false;
|
||||
t.eristaGpuVoltArray[idx] = v;
|
||||
return cust_write_table(p, &t);
|
||||
}
|
||||
|
||||
static inline bool cust_set_mariko_gpu_volt(const char* p, int idx, u32 v) {
|
||||
if (idx < 0 || idx >= 24) return false;
|
||||
CustomizeTable t;
|
||||
if (!cust_read_table(p, &t)) return false;
|
||||
t.marikoGpuVoltArray[idx] = v;
|
||||
return cust_write_table(p, &t);
|
||||
}
|
||||
|
||||
static inline u32 cust_get_field(const CustomizeTable* t, u32 offset) {
|
||||
if (!t) return 0;
|
||||
return *(u32*)((u8*)t + offset);
|
||||
}
|
||||
|
||||
#define CUST_GET_FIELD(table, field) ((table) ? (table)->field : 0)
|
||||
|
||||
static inline u32 cust_get_cust_rev(const CustomizeTable* t) { return CUST_GET_FIELD(t, custRev); }
|
||||
// static inline u32 cust_get_mtc_conf(const CustomizeTable* t) { return CUST_GET_FIELD(t, mtcConf); }
|
||||
static inline u32 cust_get_hp_mode(const CustomizeTable* t) { return CUST_GET_FIELD(t, hpMode); }
|
||||
|
||||
static inline u32 cust_get_common_emc_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, commonEmcMemVolt); }
|
||||
static inline u32 cust_get_erista_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock); }
|
||||
static inline u32 cust_get_erista_emc_max1(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock1); }
|
||||
static inline u32 cust_get_erista_emc_max2(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock2); }
|
||||
static inline u32 cust_get_mariko_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcMaxClock); }
|
||||
static inline u32 cust_get_mariko_emc_vddq(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcVddqVolt); }
|
||||
static inline u32 cust_get_emc_dvb_shift(const CustomizeTable* t) { return CUST_GET_FIELD(t, emcDvbShift); }
|
||||
|
||||
static inline u32 cust_get_tRCD(const CustomizeTable* t) { return CUST_GET_FIELD(t, t1_tRCD); }
|
||||
static inline u32 cust_get_tRP(const CustomizeTable* t) { return CUST_GET_FIELD(t, t2_tRP); }
|
||||
static inline u32 cust_get_tRAS(const CustomizeTable* t) { return CUST_GET_FIELD(t, t3_tRAS); }
|
||||
static inline u32 cust_get_tRRD(const CustomizeTable* t) { return CUST_GET_FIELD(t, t4_tRRD); }
|
||||
static inline u32 cust_get_tRFC(const CustomizeTable* t) { return CUST_GET_FIELD(t, t5_tRFC); }
|
||||
static inline u32 cust_get_tRTW(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW); }
|
||||
static inline u32 cust_get_tWTR(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR); }
|
||||
static inline u32 cust_get_tREFI(const CustomizeTable* t) { return CUST_GET_FIELD(t, t8_tREFI); }
|
||||
static inline u32 cust_get_tRTW_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW_fine_tune); }
|
||||
static inline u32 cust_get_tWTR_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR_fine_tune); }
|
||||
static inline u32 cust_get_burst_read_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_read_latency); }
|
||||
static inline u32 cust_get_burst_write_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_write_latency); }
|
||||
|
||||
static inline u32 cust_get_erista_cpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuUV); }
|
||||
static inline u32 cust_get_eristaCpuVmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuVmin); }
|
||||
static inline u32 cust_get_erista_cpu_max_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuMaxVolt); }
|
||||
static inline u32 cust_get_eristaCpuUnlock(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuUnlock); }
|
||||
|
||||
static inline u32 cust_get_mariko_cpu_uv_low(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuUVLow); }
|
||||
static inline u32 cust_get_mariko_cpu_uv_high(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuUVHigh); }
|
||||
static inline u32 cust_get_mariko_cpu_low_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuLowVmin); }
|
||||
static inline u32 cust_get_mariko_cpu_high_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuHighVmin); }
|
||||
static inline u32 cust_get_mariko_cpu_max_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuMaxVolt); }
|
||||
static inline u32 cust_get_erista_cpu_boost(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuBoostClock); }
|
||||
static inline u32 cust_get_mariko_cpu_boost(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuBoostClock); }
|
||||
static inline u32 cust_get_table_conf(const CustomizeTable* t) { return CUST_GET_FIELD(t, tableConf); }
|
||||
|
||||
static inline u32 cust_get_erista_gpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaGpuUV); }
|
||||
static inline u32 cust_get_erista_gpu_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaGpuVmin); }
|
||||
static inline u32 cust_get_mariko_gpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuUV); }
|
||||
static inline u32 cust_get_mariko_gpu_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuVmin); }
|
||||
static inline u32 cust_get_mariko_gpu_vmax(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuVmax); }
|
||||
static inline u32 cust_get_common_gpu_offset(const CustomizeTable* t) { return CUST_GET_FIELD(t, commonGpuVoltOffset); }
|
||||
static inline u32 cust_get_gpu_speedo(const CustomizeTable* t) { return CUST_GET_FIELD(t, gpuSpeedo); }
|
||||
static inline u32 cust_get_marikoCpuMaxClock(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuMaxClock); }
|
||||
|
||||
static inline u32 cust_get_erista_gpu_volt(const CustomizeTable* t, int idx) {
|
||||
if (!t || idx < 0 || idx >= 27) return 0;
|
||||
return t->eristaGpuVoltArray[idx];
|
||||
}
|
||||
|
||||
static inline u32 cust_get_mariko_gpu_volt(const CustomizeTable* t, int idx) {
|
||||
if (!t || idx < 0 || idx >= 24) return 0;
|
||||
return t->marikoGpuVoltArray[idx];
|
||||
}
|
||||
|
||||
#define DECL_ERISTA_GPU_VOLT_HELPER(freq, idx) \
|
||||
static inline bool cust_set_erista_gpu_volt_##freq( \
|
||||
const char* p, u32 v) { \
|
||||
return cust_set_erista_gpu_volt(p, idx, v); \
|
||||
}
|
||||
|
||||
#define DECL_MARIKO_GPU_VOLT_HELPER(freq, idx) \
|
||||
static inline bool cust_set_mariko_gpu_volt_##freq( \
|
||||
const char* p, u32 v) { \
|
||||
return cust_set_mariko_gpu_volt(p, idx, v); \
|
||||
}
|
||||
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(76800, 0)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(115200, 1)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(153600, 2)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(192000, 3)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(230400, 4)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(268800, 5)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(307200, 6)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(345600, 7)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(384000, 8)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(422400, 9)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(460800, 10)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(499200, 11)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(537600, 12)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(576000, 13)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(614400, 14)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(652800, 15)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(691200, 16)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(729600, 17)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(768000, 18)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(806400, 19)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(844800, 20)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(883200, 21)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(921600, 22)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(960000, 23)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(998400, 24)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(1036800, 25)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(1075200, 26)
|
||||
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(76800, 0)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(153600, 1)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(230400, 2)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(307200, 3)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(384000, 4)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(460800, 5)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(537600, 6)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(614400, 7)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(691200, 8)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(768000, 9)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(844800, 10)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(921600, 11)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(998400, 12)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1075200, 13)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1152000, 14)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1228800, 15)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1267200, 16)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1305600, 17)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1344000, 18)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1382400, 19)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1420800, 20)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1459200, 21)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1497600, 22)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1536000, 23)
|
||||
|
||||
#define DECL_ERISTA_GPU_VOLT_GET(freq, idx) \
|
||||
static inline u32 cust_get_erista_gpu_volt_##freq##_val(const char* p) { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(p, &t)) return 0; \
|
||||
return cust_get_erista_gpu_volt(&t, idx); \
|
||||
}
|
||||
#define DECL_MARIKO_GPU_VOLT_GET(freq, idx) \
|
||||
static inline u32 cust_get_mariko_gpu_volt_##freq##_val(const char* p) { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(p, &t)) return 0; \
|
||||
return cust_get_mariko_gpu_volt(&t, idx); \
|
||||
}
|
||||
|
||||
DECL_ERISTA_GPU_VOLT_GET(76800, 0)
|
||||
DECL_ERISTA_GPU_VOLT_GET(115200, 1)
|
||||
DECL_ERISTA_GPU_VOLT_GET(153600, 2)
|
||||
DECL_ERISTA_GPU_VOLT_GET(192000, 3)
|
||||
DECL_ERISTA_GPU_VOLT_GET(230400, 4)
|
||||
DECL_ERISTA_GPU_VOLT_GET(268800, 5)
|
||||
DECL_ERISTA_GPU_VOLT_GET(307200, 6)
|
||||
DECL_ERISTA_GPU_VOLT_GET(345600, 7)
|
||||
DECL_ERISTA_GPU_VOLT_GET(384000, 8)
|
||||
DECL_ERISTA_GPU_VOLT_GET(422400, 9)
|
||||
DECL_ERISTA_GPU_VOLT_GET(460800, 10)
|
||||
DECL_ERISTA_GPU_VOLT_GET(499200, 11)
|
||||
DECL_ERISTA_GPU_VOLT_GET(537600, 12)
|
||||
DECL_ERISTA_GPU_VOLT_GET(576000, 13)
|
||||
DECL_ERISTA_GPU_VOLT_GET(614400, 14)
|
||||
DECL_ERISTA_GPU_VOLT_GET(652800, 15)
|
||||
DECL_ERISTA_GPU_VOLT_GET(691200, 16)
|
||||
DECL_ERISTA_GPU_VOLT_GET(729600, 17)
|
||||
DECL_ERISTA_GPU_VOLT_GET(768000, 18)
|
||||
DECL_ERISTA_GPU_VOLT_GET(806400, 19)
|
||||
DECL_ERISTA_GPU_VOLT_GET(844800, 20)
|
||||
DECL_ERISTA_GPU_VOLT_GET(883200, 21)
|
||||
DECL_ERISTA_GPU_VOLT_GET(921600, 22)
|
||||
DECL_ERISTA_GPU_VOLT_GET(960000, 23)
|
||||
DECL_ERISTA_GPU_VOLT_GET(998400, 24)
|
||||
DECL_ERISTA_GPU_VOLT_GET(1036800, 25)
|
||||
DECL_ERISTA_GPU_VOLT_GET(1075200, 26)
|
||||
|
||||
DECL_MARIKO_GPU_VOLT_GET(76800, 0)
|
||||
DECL_MARIKO_GPU_VOLT_GET(153600, 1)
|
||||
DECL_MARIKO_GPU_VOLT_GET(230400, 2)
|
||||
DECL_MARIKO_GPU_VOLT_GET(307200, 3)
|
||||
DECL_MARIKO_GPU_VOLT_GET(384000, 4)
|
||||
DECL_MARIKO_GPU_VOLT_GET(460800, 5)
|
||||
DECL_MARIKO_GPU_VOLT_GET(537600, 6)
|
||||
DECL_MARIKO_GPU_VOLT_GET(614400, 7)
|
||||
DECL_MARIKO_GPU_VOLT_GET(691200, 8)
|
||||
DECL_MARIKO_GPU_VOLT_GET(768000, 9)
|
||||
DECL_MARIKO_GPU_VOLT_GET(844800, 10)
|
||||
DECL_MARIKO_GPU_VOLT_GET(921600, 11)
|
||||
DECL_MARIKO_GPU_VOLT_GET(998400, 12)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1075200, 13)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1152000, 14)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1228800, 15)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1267200, 16)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1305600, 17)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1344000, 18)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1382400, 19)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1420800, 20)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1459200, 21)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1497600, 22)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1536000, 23)
|
||||
|
||||
void SetKipData();
|
||||
void GetKipData();
|
||||
}
|
||||
@@ -30,12 +30,13 @@
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
#include "errors.hpp"
|
||||
#include "file_utils.hpp"
|
||||
#include "board/board.hpp"
|
||||
#include "process_management.hpp"
|
||||
#include "clock_manager.h"
|
||||
#include "ipc_service.h"
|
||||
#include "clock_manager.hpp"
|
||||
#include "ipc_service.hpp"
|
||||
#include "config.hpp"
|
||||
#define INNER_HEAP_SIZE 0x40000
|
||||
|
||||
|
||||
@@ -111,7 +112,7 @@ extern "C"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Result rc = FileUtils::Initialize();
|
||||
Result rc = fileUtils::Initialize();
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
fatalThrow(rc);
|
||||
@@ -125,44 +126,44 @@ int main(int argc, char** argv)
|
||||
|
||||
processManagement::WaitForQLaunch();
|
||||
|
||||
ClockManager* clockMgr = new ClockManager();
|
||||
IpcService* ipcSrv = new IpcService(clockMgr);
|
||||
clockManager::Initialize();
|
||||
ipcService::Initialize();
|
||||
|
||||
FileUtils::LogLine("Ready");
|
||||
fileUtils::LogLine("Ready");
|
||||
|
||||
clockMgr->SetRunning(true);
|
||||
clockMgr->GetConfig()->SetEnabled(true);
|
||||
ipcSrv->SetRunning(true);
|
||||
clockManager::SetRunning(true);
|
||||
config::SetEnabled(true);
|
||||
ipcService::SetRunning(true);
|
||||
// TemperaturePoint *table;
|
||||
// ReadConfigFile(&table);
|
||||
// InitFanController(table);
|
||||
// StartFanControllerThread();
|
||||
|
||||
while (clockMgr->Running())
|
||||
while (clockManager::Running())
|
||||
{
|
||||
clockMgr->Tick();
|
||||
clockMgr->WaitForNextTick();
|
||||
clockManager::Tick();
|
||||
clockManager::WaitForNextTick();
|
||||
}
|
||||
|
||||
ipcSrv->SetRunning(false);
|
||||
delete ipcSrv;
|
||||
delete clockMgr;
|
||||
ipcService::SetRunning(false);
|
||||
ipcService::Exit();
|
||||
clockManager::Exit();
|
||||
processManagement::Exit();
|
||||
board::Exit();
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
FileUtils::LogLine("[!] %s", ex.what());
|
||||
fileUtils::LogLine("[!] %s", ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::exception_ptr p = std::current_exception();
|
||||
FileUtils::LogLine("[!?] %s", p ? p.__cxa_exception_type()->name() : "...");
|
||||
fileUtils::LogLine("[!?] %s", p ? p.__cxa_exception_type()->name() : "...");
|
||||
}
|
||||
|
||||
FileUtils::LogLine("Exit");
|
||||
fileUtils::LogLine("Exit");
|
||||
svcSleepThread(1000000ULL);
|
||||
FileUtils::Exit();
|
||||
fileUtils::Exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
|
||||
|
||||
#include "process_management.hpp"
|
||||
#include "file_utils.h"
|
||||
#include "errors.h"
|
||||
#include "file_utils.hpp"
|
||||
#include "errors.hpp"
|
||||
|
||||
namespace processManagement {
|
||||
|
||||
|
||||
@@ -514,7 +514,7 @@ std::uint32_t Board::GetHz(SysClkModule module)
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
|
||||
rc = clkrstGetClockRate(&session, &hz);
|
||||
ASSERT_RESULT_OK(rc, "clkrstSetClockRate");
|
||||
ASSERT_RESULT_OK(rc, "clkrstGetClockRate");
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user