diff --git a/k11_extension/source/svc/KernelSetState.c b/k11_extension/source/svc/KernelSetState.c
index 2b944aa..aeb6372 100644
--- a/k11_extension/source/svc/KernelSetState.c
+++ b/k11_extension/source/svc/KernelSetState.c
@@ -109,20 +109,16 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
if(rosalinaState & 2)
hasStartedRosalinaNetworkFuncsOnce = true;
- // 1: all applet/app/gsp/dsp... threads 4: hid/ir
- if(varg1 & 1)
+ // 1: all applet/app/dsp/csnd... threads 2: gsp 4: hid/ir
+ for (u32 v = 4; v != 0; v >>= 1)
{
- if (rosalinaState & 1)
- rosalinaLockThreads(1);
- else
- rosalinaUnlockThreads(1);
- }
- if(varg1 & 4)
- {
- if (rosalinaState & 4)
- rosalinaLockThreads(4);
- else
- rosalinaUnlockThreads(4);
+ if (varg1 & v)
+ {
+ if (rosalinaState & v)
+ rosalinaLockThreads(v);
+ else
+ rosalinaUnlockThreads(v);
+ }
}
break;
diff --git a/k11_extension/source/synchronization.c b/k11_extension/source/synchronization.c
index d6dc6cb..4b00072 100644
--- a/k11_extension/source/synchronization.c
+++ b/k11_extension/source/synchronization.c
@@ -104,10 +104,18 @@ bool rosalinaThreadLockPredicate(KThread *thread, u32 mask)
if (highTitleId != 0x00040130) // non-sysmodules
return true;
else
- return lowTitleId == 0x1A02 || lowTitleId == 0x1C02 || lowTitleId == 0x2702; // dsp, gsp, csnd
+ return lowTitleId == 0x1A02 || lowTitleId == 0x2702; // dsp, csnd
+ }
+ if (mask & 2)
+ {
+ if (highTitleId != 0x00040130) // non-sysmodules
+ false;
+ return lowTitleId == 0x1C02; // gsp
}
if (mask & 4)
{
+ if (highTitleId != 0x00040130) // non-sysmodules
+ return false;
return lowTitleId == 0x1D02 || lowTitleId == 0x3302;
}
diff --git a/sysmodules/rosalina/include/luminance.h b/sysmodules/rosalina/include/luminance.h
new file mode 100644
index 0000000..3590cc3
--- /dev/null
+++ b/sysmodules/rosalina/include/luminance.h
@@ -0,0 +1,31 @@
+/*
+* This file is part of Luma3DS
+* Copyright (C) 2016-2020 Aurora Wright, TuxSH
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that 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 .
+*
+* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+* * Requiring preservation of specified reasonable legal notices or
+* author attributions in that material or in the Appropriate Legal
+* Notices displayed by works containing it.
+* * Prohibiting misrepresentation of the origin of that material,
+* or requiring that modified versions of such material be marked in
+* reasonable ways as different from the original version.
+*/
+
+#pragma once
+
+#include <3ds/types.h>
+
+u32 getCurrentLuminance(bool top);
diff --git a/sysmodules/rosalina/source/luminance.c b/sysmodules/rosalina/source/luminance.c
new file mode 100644
index 0000000..3e3ea8c
--- /dev/null
+++ b/sysmodules/rosalina/source/luminance.c
@@ -0,0 +1,110 @@
+/*
+* This file is part of Luma3DS
+* Copyright (C) 2016-2020 Aurora Wright, TuxSH
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that 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 .
+*
+* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+* * Requiring preservation of specified reasonable legal notices or
+* author attributions in that material or in the Appropriate Legal
+* Notices displayed by works containing it.
+* * Prohibiting misrepresentation of the origin of that material,
+* or requiring that modified versions of such material be marked in
+* reasonable ways as different from the original version.
+*/
+
+#include <3ds.h>
+#include
+#include "luminance.h"
+#include "utils.h"
+
+extern bool isN3DS;
+
+typedef struct BlPwmData
+{
+ float coeffs[3][3];
+ u8 numLevels;
+ u8 unk;
+ u16 luminanceLevels[7];
+ u16 brightnessMax;
+ u16 brightnessMin;
+} BlPwmData;
+
+// Calibration, with (dubious) default values as fallback
+static BlPwmData s_blPwmData = {
+ .coeffs = {
+ { 0.00111639f, 1.41412f, 0.07178809f },
+ { 0.000418169f, 0.66567f, 0.06098654f },
+ { 0.00208543f, 1.55639f, 0.0385939 }
+ },
+ .numLevels = 5,
+ .unk = 0,
+ .luminanceLevels = { 20, 43, 73, 95, 117, 172, 172 },
+ .brightnessMax = 512,
+ .brightnessMin = 13,
+};
+
+static inline float getPwmRatio(u32 brightnessMax, u32 pwmCnt)
+{
+ u32 val = (pwmCnt & 0x10000) ? pwmCnt & 0x3FF : 511; // check pwm enabled flag
+ return (float)brightnessMax / (val + 1);
+}
+
+// nn's asm has rounding errors (originally at 10^-3)
+static inline u32 luminanceToBrightness(u32 luminance, const float coeffs[3], u32 minLuminance, float pwmRatio)
+{
+ float x = (float)luminance;
+ float y = coeffs[0]*x*x + coeffs[1]*x + coeffs[2];
+ y = (y <= minLuminance ? (float)minLuminance : y) / pwmRatio;
+
+ return (u32)(y + 0.5f);
+}
+
+static inline u32 brightnessToLuminance(u32 brightness, const float coeffs[3], float pwmRatio)
+{
+ // Find polynomial root of ax^2 + bx + c = y
+
+ float y = (float)brightness * pwmRatio;
+ float a = coeffs[0];
+ float b = coeffs[1];
+ float c = coeffs[2] - y;
+
+ float x0 = (-b + sqrtf(b*b - 4.0f*a*c)) / (a + a);
+
+ return (u32)(x0 + 0.5f);
+}
+
+static void readCalibration(void)
+{
+ static bool calibRead = false;
+
+ if (!calibRead) {
+ cfguInit();
+ calibRead = R_SUCCEEDED(CFG_GetConfigInfoBlk8(sizeof(BlPwmData), 0x50002, &s_blPwmData));
+ cfguExit();
+ }
+}
+
+u32 getCurrentLuminance(bool top)
+{
+ u32 regbase = top ? 0x10202200 : 0x10202A00;
+
+ readCalibration();
+
+ const float *coeffs = s_blPwmData.coeffs[top ? (isN3DS ? 2 : 1) : 0];
+ u32 brightness = REG32(regbase + 0x40);
+ float ratio = getPwmRatio(s_blPwmData.brightnessMax, REG32(regbase + 0x44));
+
+ return brightnessToLuminance(brightness, coeffs, ratio);
+}
diff --git a/sysmodules/rosalina/source/menu.c b/sysmodules/rosalina/source/menu.c
index 04c9779..9825461 100644
--- a/sysmodules/rosalina/source/menu.c
+++ b/sysmodules/rosalina/source/menu.c
@@ -211,13 +211,13 @@ void menuEnter(void)
if(!menuShouldExit && menuRefCount == 0)
{
menuRefCount++;
- svcKernelSetState(0x10000, 1);
+ svcKernelSetState(0x10000, 2 | 1);
svcSleepThread(5 * 1000 * 100LL);
if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
{
// Oops
menuRefCount = 0;
- svcKernelSetState(0x10000, 1);
+ svcKernelSetState(0x10000, 2 | 1);
svcSleepThread(5 * 1000 * 100LL);
}
else
@@ -235,7 +235,7 @@ void menuLeave(void)
{
Draw_RestoreFramebuffer();
Draw_FreeFramebufferCache();
- svcKernelSetState(0x10000, 1);
+ svcKernelSetState(0x10000, 2 | 1);
}
Draw_Unlock();
}
diff --git a/sysmodules/rosalina/source/menus.c b/sysmodules/rosalina/source/menus.c
index 0986802..901329a 100644
--- a/sysmodules/rosalina/source/menus.c
+++ b/sysmodules/rosalina/source/menus.c
@@ -39,6 +39,7 @@
#include "memory.h"
#include "fmt.h"
#include "process_patches.h"
+#include "luminance.h"
Menu rosalinaMenu = {
"Rosalina menu",
@@ -155,107 +156,92 @@ void RosalinaMenu_Reboot(void)
while(!menuShouldExit);
}
-static u32 gspPatchAddrN3ds, gspPatchValuesN3ds[2];
-static bool gspPatchDoneN3ds;
-
-static Result RosalinaMenu_PatchN3dsGspForBrightness(u32 size)
-{
- u32 *off = (u32 *)0x00100000;
- u32 *end = (u32 *)(0x00100000 + size);
-
- for (; off < end && (off[0] != 0xE92D4030 || off[1] != 0xE1A04000 || off[2] != 0xE2805C01 || off[3] != 0xE5D0018C); off++);
-
- if (off >= end) {
- return -1;
- }
-
- gspPatchAddrN3ds = (u32)off;
- gspPatchValuesN3ds[0] = off[26];
- gspPatchValuesN3ds[1] = off[50];
-
- // NOP brightness changing in GSP
- off[26] = 0xE1A00000;
- off[50] = 0xE1A00000;
-
- return 0;
-}
-static Result RosalinaMenu_RevertN3dsGspPatch(u32 size)
-{
- (void)size;
-
- u32 *off = (u32 *)gspPatchAddrN3ds;
- off[26] = gspPatchValuesN3ds[0];
- off[50] = gspPatchValuesN3ds[1];
-
- return 0;
-}
-
void RosalinaMenu_ChangeScreenBrightness(void)
{
- Result patchResult = 0;
- if (isN3DS && !gspPatchDoneN3ds)
- {
- patchResult = PatchProcessByName("gsp", RosalinaMenu_PatchN3dsGspForBrightness);
- gspPatchDoneN3ds = R_SUCCEEDED(patchResult);
- }
-
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_Unlock();
+ // gsp:LCD GetLuminance is stubbed on O3DS so we have to implement it ourselves... damn it.
+ // Assume top and bottom screen luminances are the same (should be; if not, we'll set them to the same values).
+ u32 luminance = getCurrentLuminance(false);
+
do
{
- // Assume the current brightness for both screens are the same.
- s32 brightness = (s32)(LCD_TOP_BRIGHTNESS & 0xFF);
-
Draw_Lock();
Draw_DrawString(10, 10, COLOR_TITLE, "Rosalina menu");
u32 posY = 30;
- posY = Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Current brightness (0..255): %3lu\n\n", brightness);
- if (R_SUCCEEDED(patchResult))
- {
- posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press Up/Down for +-1, Right/Left for +-10.\n");
- posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press Y to revert the GSP patch and exit.\n\n");
-
- posY = Draw_DrawString(10, posY, COLOR_RED, "WARNING: \n");
- posY = Draw_DrawString(10, posY, COLOR_WHITE, " * avoid using values far higher than the presets.\n");
- posY = Draw_DrawString(10, posY, COLOR_WHITE, " * normal brightness mngmt. is now broken on N3DS.\nYou'll need to press Y to revert");
- }
- else
- Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Failed to patch GSP (0x%08lx).", (u32)patchResult);
+ posY = Draw_DrawFormattedString(10, posY, COLOR_WHITE, "Current luminance: %lu\n\n", luminance);
+ posY = Draw_DrawString(10, posY, COLOR_WHITE, "Controls: Up/Down for +-1, Right/Left for +-10.\n");
+ posY = Draw_DrawString(10, posY, COLOR_WHITE, "Press A to start, B to exit.\n\n");
+ posY = Draw_DrawString(10, posY, COLOR_RED, "WARNING: \n");
+ posY = Draw_DrawString(10, posY, COLOR_WHITE, " * value will be limited by the presets.\n");
+ posY = Draw_DrawString(10, posY, COLOR_WHITE, " * bottom framebuffer will be restored until\nyou exit.");
Draw_FlushFramebuffer();
Draw_Unlock();
u32 pressed = waitInputWithTimeout(1000);
- if ((pressed & DIRECTIONAL_KEYS) && R_SUCCEEDED(patchResult))
- {
- if (pressed & KEY_UP)
- brightness += 1;
- else if (pressed & KEY_DOWN)
- brightness -= 1;
- else if (pressed & KEY_RIGHT)
- brightness += 10;
- else if (pressed & KEY_LEFT)
- brightness -= 10;
+ if (pressed & KEY_A)
+ break;
- brightness = brightness < 0 ? 0 : brightness;
- brightness = brightness > 255 ? 255 : brightness;
- LCD_TOP_BRIGHTNESS = (u32)brightness;
- LCD_BOT_BRIGHTNESS = (u32)brightness;
- }
- else if ((pressed & KEY_Y) && gspPatchDoneN3ds)
- {
- patchResult = PatchProcessByName("gsp", RosalinaMenu_RevertN3dsGspPatch);
- gspPatchDoneN3ds = !R_SUCCEEDED(patchResult);
- return;
- }
- else if (pressed & KEY_B)
+ if (pressed & KEY_B)
return;
}
while (!menuShouldExit);
+
+ Draw_Lock();
+
+ Draw_RestoreFramebuffer();
+ Draw_FreeFramebufferCache();
+
+ svcKernelSetState(0x10000, 2); // unblock gsp
+ gspLcdInit(); // assume it doesn't fail. If it does, brightness won't change, anyway.
+
+ // gsp:LCD will normalize the brightness between top/bottom screen, handle PWM, etc.
+
+ s32 lum = (s32)luminance;
+
+ do
+ {
+ u32 pressed = waitInputWithTimeout(1000);
+ if (pressed & DIRECTIONAL_KEYS)
+ {
+ if (pressed & KEY_UP)
+ lum += 1;
+ else if (pressed & KEY_DOWN)
+ lum -= 1;
+ else if (pressed & KEY_RIGHT)
+ lum += 10;
+ else if (pressed & KEY_LEFT)
+ lum -= 10;
+
+ lum = lum < 0 ? 0 : lum;
+
+ // We need to call gsp here because updating the active duty LUT is a bit tedious (plus, GSP has internal state).
+ // This is actually SetLuminance:
+ GSPLCD_SetBrightnessRaw(BIT(GSP_SCREEN_TOP) | BIT(GSP_SCREEN_BOTTOM), lum);
+ }
+
+ if (pressed & KEY_B)
+ break;
+ }
+ while (!menuShouldExit);
+
+ gspLcdExit();
+ svcKernelSetState(0x10000, 2); // block gsp again
+
+ if (R_FAILED(Draw_AllocateFramebufferCache(FB_BOTTOM_SIZE)))
+ {
+ // Shouldn't happen
+ __builtin_trap();
+ }
+ else
+ Draw_SetupFramebuffer();
+
+ Draw_Unlock();
}
void RosalinaMenu_PowerOff(void) // Soft shutdown.
diff --git a/sysmodules/rosalina/source/menus/debugger.c b/sysmodules/rosalina/source/menus/debugger.c
index 70bf67a..0a00ebf 100644
--- a/sysmodules/rosalina/source/menus/debugger.c
+++ b/sysmodules/rosalina/source/menus/debugger.c
@@ -102,7 +102,6 @@ Result debuggerDisable(s64 timeout)
svcCloseHandle(dummy);
PMDBG_DebugNextApplicationByForce(false);
nextApplicationGdbCtx = NULL;
- svcKernelSetState(0x10000, 2);
}
return res;
diff --git a/sysmodules/rosalina/source/minisoc.c b/sysmodules/rosalina/source/minisoc.c
index 4d698be..77ef571 100644
--- a/sysmodules/rosalina/source/minisoc.c
+++ b/sysmodules/rosalina/source/minisoc.c
@@ -102,7 +102,7 @@ Result miniSocInit(void)
ret = SOCU_Initialize(miniSocMemHandle, socContextSize);
if(ret != 0) goto cleanup;
- svcKernelSetState(0x10000, 2);
+ svcKernelSetState(0x10000, 0x10);
miniSocEnabled = true;
srvExtAddToNdmuWorkaroundCount(1);
@@ -149,7 +149,7 @@ Result miniSocExitDirect(void)
{
miniSocEnabled = false;
srvExtAddToNdmuWorkaroundCount(-1);
- svcKernelSetState(0x10000, 2);
+ svcKernelSetState(0x10000, 0x10);
}
return ret;
}