mirror of
https://github.com/LumaTeam/Luma3DS.git
synced 2026-02-22 01:44:38 +00:00
Closes #2137. Even though (when running TWL/AGB FIRM) the SoC is in O3DS mode, and the GPU also is, as well as most other components behaving as such (external RAM, L2C not usable, etc.), this is NOT the case for the LCD and adaptive backlight logic which retains FULL N3DS functionality, including a feature where the window is blended with a given color depending on the overall relative luminance of that window. However, Nintendo's own code mistakenly assumes the opposite, and clearly so ("if GPU in N3DS mode" checks, not passing N3DS extra adaptive backlight (ABL) to TWL/AGB_FIRM). This has implications: - Powersaving (ABL) settings in TWL/AGB_FIRM is inconsistent with *both* O3DS (because the new RGB blend LUT has been set to its current value by NATIVE_FIRM) and N3DS (because "pwn_cnt" and "inertia" are missing their N3DS-only bits) - "rave party" when booting into TWL/AGB_FIRM or O3DS NATIVE_FIRM without these regs (well, the LUT) initialized. Easiest way to do so is by leveraging the "DSi autooboot" feature Luma provides. It is worth noting at least the LUT survives hardware reboots (if Nintendo were using DSi software that was using TLNC-based reboots, they wouldn't have noticed). Only touch the autoboot path, for now
260 lines
8.8 KiB
C
260 lines
8.8 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Screen init code by dark_samus, bil1s, Normmatt, delebile and others
|
|
* LCD deinit code by tiniVi
|
|
*/
|
|
|
|
#include "types.h"
|
|
#include "memory.h"
|
|
|
|
void prepareForFirmlaunch(void);
|
|
extern u32 prepareForFirmlaunchSize;
|
|
|
|
extern volatile Arm11Operation operation;
|
|
|
|
static void initScreens(u32 brightnessLevel, struct fb *fbs)
|
|
{
|
|
*(vu32 *)0x10141200 = 0x1007F;
|
|
|
|
*(vu32 *)0x10202204 = 0x01000000; //set LCD fill black to hide potential garbage -- NFIRM does it before firmlaunching
|
|
*(vu32 *)0x10202A04 = 0x01000000;
|
|
|
|
*(vu32 *)0x10202014 = 0x00000001;
|
|
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
|
|
*(vu32 *)0x10202240 = brightnessLevel;
|
|
*(vu32 *)0x10202A40 = brightnessLevel;
|
|
*(vu32 *)0x10202244 = 0x1023E;
|
|
*(vu32 *)0x10202A44 = 0x1023E;
|
|
|
|
//Top screen
|
|
*(vu32 *)0x10400400 = 0x000001c2;
|
|
*(vu32 *)0x10400404 = 0x000000d1;
|
|
*(vu32 *)0x10400408 = 0x000001c1;
|
|
*(vu32 *)0x1040040c = 0x000001c1;
|
|
*(vu32 *)0x10400410 = 0x00000000;
|
|
*(vu32 *)0x10400414 = 0x000000cf;
|
|
*(vu32 *)0x10400418 = 0x000000d1;
|
|
*(vu32 *)0x1040041c = 0x01c501c1;
|
|
*(vu32 *)0x10400420 = 0x00010000;
|
|
*(vu32 *)0x10400424 = 0x0000019d;
|
|
*(vu32 *)0x10400428 = 0x00000002;
|
|
*(vu32 *)0x1040042c = 0x00000192;
|
|
*(vu32 *)0x10400430 = 0x00000192;
|
|
*(vu32 *)0x10400434 = 0x00000192;
|
|
*(vu32 *)0x10400438 = 0x00000001;
|
|
*(vu32 *)0x1040043c = 0x00000002;
|
|
*(vu32 *)0x10400440 = 0x01960192;
|
|
*(vu32 *)0x10400444 = 0x00000000;
|
|
*(vu32 *)0x10400448 = 0x00000000;
|
|
*(vu32 *)0x1040045C = 0x00f00190;
|
|
*(vu32 *)0x10400460 = 0x01c100d1;
|
|
*(vu32 *)0x10400464 = 0x01920002;
|
|
*(vu32 *)0x10400468 = (u32)fbs[0].top_left;
|
|
*(vu32 *)0x1040046C = (u32)fbs[1].top_left;
|
|
*(vu32 *)0x10400470 = 0x80341;
|
|
*(vu32 *)0x10400474 = 0x00010501;
|
|
*(vu32 *)0x10400478 = 0;
|
|
*(vu32 *)0x10400494 = (u32)fbs[0].top_right;
|
|
*(vu32 *)0x10400498 = (u32)fbs[1].top_right;
|
|
*(vu32 *)0x10400490 = 0x000002D0;
|
|
*(vu32 *)0x1040049C = 0x00000000;
|
|
|
|
// Color LUT
|
|
*(vu32 *)0x10400480 = 0;
|
|
for(u32 i = 0; i < 256; i++)
|
|
*(vu32 *)0x10400484 = 0x10101 * i;
|
|
|
|
//Bottom screen
|
|
*(vu32 *)0x10400500 = 0x000001c2;
|
|
*(vu32 *)0x10400504 = 0x000000d1;
|
|
*(vu32 *)0x10400508 = 0x000001c1;
|
|
*(vu32 *)0x1040050c = 0x000001c1;
|
|
*(vu32 *)0x10400510 = 0x000000cd;
|
|
*(vu32 *)0x10400514 = 0x000000cf;
|
|
*(vu32 *)0x10400518 = 0x000000d1;
|
|
*(vu32 *)0x1040051c = 0x01c501c1;
|
|
*(vu32 *)0x10400520 = 0x00010000;
|
|
*(vu32 *)0x10400524 = 0x0000019d;
|
|
*(vu32 *)0x10400528 = 0x00000052;
|
|
*(vu32 *)0x1040052c = 0x00000192;
|
|
*(vu32 *)0x10400530 = 0x00000192;
|
|
*(vu32 *)0x10400534 = 0x0000004f;
|
|
*(vu32 *)0x10400538 = 0x00000050;
|
|
*(vu32 *)0x1040053c = 0x00000052;
|
|
*(vu32 *)0x10400540 = 0x01980194;
|
|
*(vu32 *)0x10400544 = 0x00000000;
|
|
*(vu32 *)0x10400548 = 0x00000011;
|
|
*(vu32 *)0x1040055C = 0x00f00140;
|
|
*(vu32 *)0x10400560 = 0x01c100d1;
|
|
*(vu32 *)0x10400564 = 0x01920052;
|
|
*(vu32 *)0x10400568 = (u32)fbs[0].bottom;
|
|
*(vu32 *)0x1040056C = (u32)fbs[1].bottom;
|
|
*(vu32 *)0x10400570 = 0x80301;
|
|
*(vu32 *)0x10400574 = 0x00010501;
|
|
*(vu32 *)0x10400578 = 0;
|
|
*(vu32 *)0x10400590 = 0x000002D0;
|
|
*(vu32 *)0x1040059C = 0x00000000;
|
|
|
|
// Color LUT
|
|
*(vu32 *)0x10400580 = 0;
|
|
for(u32 i = 0; i < 256; i++)
|
|
*(vu32 *)0x10400584 = 0x10101 * i;
|
|
|
|
*(vu32 *)0x10202204 = 0x00000000; //unset LCD fill
|
|
*(vu32 *)0x10202A04 = 0x00000000;
|
|
}
|
|
|
|
static void setupFramebuffers(struct fb *fbs)
|
|
{
|
|
*(vu32 *)0x10202204 = 0x01000000; //set LCD fill black to hide potential garbage -- NFIRM does it before firmlaunching
|
|
*(vu32 *)0x10202A04 = 0x01000000;
|
|
|
|
*(vu32 *)0x10400468 = (u32)fbs[0].top_left;
|
|
*(vu32 *)0x1040046c = (u32)fbs[1].top_left;
|
|
*(vu32 *)0x10400494 = (u32)fbs[0].top_right;
|
|
*(vu32 *)0x10400498 = (u32)fbs[1].top_right;
|
|
*(vu32 *)0x10400568 = (u32)fbs[0].bottom;
|
|
*(vu32 *)0x1040056c = (u32)fbs[1].bottom;
|
|
|
|
//Set framebuffer format, framebuffer select and stride
|
|
*(vu32 *)0x10400470 = 0x80341;
|
|
*(vu32 *)0x10400478 = 0;
|
|
*(vu32 *)0x10400490 = 0x2D0;
|
|
*(vu32 *)0x10400570 = 0x80301;
|
|
*(vu32 *)0x10400578 = 0;
|
|
*(vu32 *)0x10400590 = 0x2D0;
|
|
|
|
*(vu32 *)0x10202204 = 0x00000000; //unset LCD fill
|
|
*(vu32 *)0x10202A04 = 0x00000000;
|
|
}
|
|
|
|
static void clearScreens(struct fb *fb)
|
|
{
|
|
//Setting up two simultaneous memory fills using the GPU
|
|
|
|
vu32 *REGs_PSC0 = (vu32 *)0x10400010,
|
|
*REGs_PSC1 = (vu32 *)0x10400020;
|
|
|
|
REGs_PSC0[0] = (u32)fb->top_left >> 3; //Start address
|
|
REGs_PSC0[1] = (u32)(fb->top_left + SCREEN_TOP_FBSIZE) >> 3; //End address
|
|
REGs_PSC0[2] = 0; //Fill value
|
|
REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start
|
|
|
|
REGs_PSC1[0] = (u32)fb->bottom >> 3; //Start address
|
|
REGs_PSC1[1] = (u32)(fb->bottom + SCREEN_BOTTOM_FBSIZE) >> 3; //End address
|
|
REGs_PSC1[2] = 0; //Fill value
|
|
REGs_PSC1[3] = (2 << 8) | 1; //32-bit pattern; start
|
|
|
|
while(!((REGs_PSC0[3] & 2) && (REGs_PSC1[3] & 2)));
|
|
}
|
|
|
|
static void swapFramebuffers(bool isAlternate)
|
|
{
|
|
u32 isAlternateTmp = isAlternate ? 1 : 0;
|
|
*(vu32 *)0x10400478 = (*(vu32 *)0x10400478 & 0xFFFFFFFE) | isAlternateTmp;
|
|
*(vu32 *)0x10400578 = (*(vu32 *)0x10400578 & 0xFFFFFFFE) | isAlternateTmp;
|
|
}
|
|
|
|
static void updateBrightness(u32 brightnessLevel)
|
|
{
|
|
//Change brightness
|
|
*(vu32 *)0x10202240 = brightnessLevel;
|
|
*(vu32 *)0x10202A40 = brightnessLevel;
|
|
}
|
|
|
|
static void deinitScreens(void)
|
|
{
|
|
//Shutdown LCDs
|
|
*(vu32 *)0x10202A44 = 0;
|
|
*(vu32 *)0x10202244 = 0;
|
|
*(vu32 *)0x10202014 = 0;
|
|
}
|
|
|
|
static void zerofillN3dsAblRegisters(void)
|
|
{
|
|
// It should be fine to write to these regs even on O3DS as they
|
|
// are RAZ/WI
|
|
|
|
// TODO: read from calibration, but null values should do just
|
|
// fine. From testing, LUT explicitly ignores null values, and
|
|
// it is probably the case of reg @ 0x54 as well.
|
|
*(vu32 *)0x10202250 = 0; // unknown 24-bit value, seen: 0
|
|
*(vu32 *)0x10202254 = 0; // unknown 24-bit value, seen: nonzero
|
|
|
|
*(vu32 *)0x10202A50 = 0; // unknown 24-bit value, seen: 0
|
|
*(vu32 *)0x10202A54 = 0; // unknown 24-bit value, seen: nonzero
|
|
|
|
for (u32 i = 0; i < 64; i++) {
|
|
// Blend colors (w/ color multiplication) for each group
|
|
// of 4 relative-luminance Rs
|
|
*(vu32 *)(0x10202300 + 4*i) = 0;
|
|
*(vu32 *)(0x10202B00 + 4*i) = 0;
|
|
}
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
operation = ARM11_READY;
|
|
|
|
while(true)
|
|
{
|
|
switch(operation)
|
|
{
|
|
case ARM11_READY:
|
|
continue;
|
|
case INIT_SCREENS:
|
|
initScreens(*(vu32 *)ARM11_PARAMETERS_ADDRESS, (struct fb *)(ARM11_PARAMETERS_ADDRESS + 4));
|
|
break;
|
|
case SETUP_FRAMEBUFFERS:
|
|
setupFramebuffers((struct fb *)ARM11_PARAMETERS_ADDRESS);
|
|
break;
|
|
case CLEAR_SCREENS:
|
|
clearScreens((struct fb *)ARM11_PARAMETERS_ADDRESS);
|
|
break;
|
|
case SWAP_FRAMEBUFFERS:
|
|
swapFramebuffers(*(volatile bool *)ARM11_PARAMETERS_ADDRESS);
|
|
break;
|
|
case UPDATE_BRIGHTNESS:
|
|
updateBrightness(*(vu32 *)ARM11_PARAMETERS_ADDRESS);
|
|
break;
|
|
case DEINIT_SCREENS:
|
|
deinitScreens();
|
|
break;
|
|
case ZEROFILL_N3DS_ABL_REGISTERS:
|
|
zerofillN3dsAblRegisters();
|
|
break;
|
|
case PREPARE_ARM11_FOR_FIRMLAUNCH:
|
|
memcpy((void *)0x1FFFFC00, (void *)prepareForFirmlaunch, prepareForFirmlaunchSize);
|
|
*(vu32 *)0x1FFFFFFC = 0;
|
|
((void (*)(u32, volatile Arm11Operation *))0x1FFFFC00)(ARM11_READY, &operation);
|
|
}
|
|
|
|
operation = ARM11_READY;
|
|
}
|
|
}
|