Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) Improved Sleep Mode #42

Open
wants to merge 1 commit into
base: kernel_experiments
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/arm11/hardware/codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ void CODEC_deinit(void);
*/
void CODEC_wakeup(void);

void CODEC_muteI2S(void);

void CODEC_unmuteI2S(void);

/**
* @brief Get raw ADC data for Circle-Pad/Touchscreen.
*
Expand Down
1 change: 1 addition & 0 deletions include/arm11/hardware/lcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,6 @@ typedef enum
u8 LCDI2C_readReg(u8 lcd, LcdI2cReg reg);
void LCDI2C_writeReg(u8 lcd, LcdI2cReg reg, u8 data);
void LCDI2C_init(void);
void LCDI2C_deinit(void);
void LCDI2C_waitBacklightsOn(void);
u16 LCDI2C_getRevisions(void);
3 changes: 3 additions & 0 deletions include/arm9/arm7_stub.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
noreturn void _a7_overlay_stub(void);
extern const u32 _a7_overlay_stub_size[];

noreturn void _a7_overlay_stub_capture(void);
extern const u32 _a7_overlay_stub_capture_size[];

noreturn void _a7_stub_start(void);
extern u16 _a7_stub9_swi[]; // Final ARM9 mem location.
extern const u32 _a7_stub_size[];
2 changes: 1 addition & 1 deletion include/arm9/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@

noreturn void panic();
noreturn void panicMsg(const char *msg);
//void dumpMem(u8 *mem, u32 size, char *filepath);
void dumpMem(u8 *mem, u32 size, char *filepath);
3 changes: 3 additions & 0 deletions include/hardware/lgy.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,11 @@ Result LGY_prepareGbaMode(bool biosIntro, u16 saveType, const char *const savePa
Result LGY_setGbaRtc(const GbaRtc rtc);
Result LGY_getGbaRtc(GbaRtc *const out);
Result LGY_backupGbaSave(void);
void LGY_sleepGba(void);
void LGY_wakeGba(void);
#ifdef ARM11
void LGY_switchMode(void);
void LGY_handleOverrides(void);
void LGY_deinit(void);
int LGY_isSleeping(void);
#endif
2 changes: 2 additions & 0 deletions include/ipc_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ typedef enum
IPC_CMD9_SET_GBA_RTC = MAKE_CMD9(0, 0, 2),
IPC_CMD9_GET_GBA_RTC = MAKE_CMD9(0, 1, 0),
IPC_CMD9_BACKUP_GBA_SAVE = MAKE_CMD9(0, 0, 0),
IPC_CMD9_SLEEPGBA = MAKE_CMD9(0, 0, 0),
IPC_CMD9_WAKEGBA = MAKE_CMD9(0, 0, 0),

// Miscellaneous API.
IPC_CMD9_PREPARE_POWER = MAKE_CMD9(0, 0, 0) // Also used for panic() and guruMeditation().
Expand Down
16 changes: 16 additions & 0 deletions source/arm11/hardware/codec.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,22 @@ void CODEC_wakeup(void)
TIMER_sleepMs(18); // Fixed 18 ms delay when unsetting this GPIO.
}

void CODEC_muteI2S(void)
{
codecMaskReg(0x00, 0x3F, 0x00, 0xC0);
codecWriteReg(0x00, 0x40, 0x0C);

codecMaskReg(0x64, 0x77, 0xC, 0xC);
}

void CODEC_unmuteI2S(void)
{
codecMaskReg(0x00, 0x3F, 0xC0, 0xC0);
codecWriteReg(0x00, 0x40, 0x00);

codecMaskReg(0x64, 0x77, 0x0, 0xC);
}

bool CODEC_getRawAdcData(CdcAdcData *data)
{
if((codecReadReg(0x67, 0x26) & 2u) == 0)
Expand Down
4 changes: 2 additions & 2 deletions source/arm11/hardware/gfx.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ void GFX_powerOnBacklights(GfxBlight mask)

mask <<= 1;
MCU_controlLCDPower(mask); // Power on backlights.
if(MCU_waitEvents(0x3Fu<<24) != (u32)mask<<24) panic();
//if(MCU_waitEvents(0x3Fu<<24) != (u32)mask<<24) panic();
}

void GFX_powerOffBacklights(GfxBlight mask)
Expand All @@ -374,7 +374,7 @@ void GFX_powerOffBacklights(GfxBlight mask)
g_gfxState.lcdPower &= ~mask;

MCU_controlLCDPower(mask); // Power off backlights.
if(MCU_waitEvents(0x3Fu<<24) != (u32)mask<<24) panic();
//if(MCU_waitEvents(0x3Fu<<24) != (u32)mask<<24) panic();
}

void GFX_setBrightness(u8 top, u8 bot)
Expand Down
65 changes: 59 additions & 6 deletions source/arm11/hardware/lgy.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
#include "types.h"
#include "hardware/lgy.h"
#include "hardware/pxi.h"
#include "hardware/gfx.h"
#include "ipc_handler.h"
#include "arm11/hardware/hid.h"
#include "arm11/hardware/interrupt.h"
#include "arm11/hardware/timer.h"
#include "hardware/cache.h"
#include "arm11/hardware/pdn.h"
#include "arm11/hardware/mcu.h"
#include "arm11/fmt.h"


#define LGY_REGS_BASE (IO_MEM_ARM9_ARM11 + 0x41100)
Expand All @@ -41,22 +44,23 @@
#define REG_LGY_UNK3 *(( vu8*)(LGY_REGS_BASE + 0x19)) // DSi gamecard detection value?
#define REG_LGY_UNK4 *((const vu8*)(LGY_REGS_BASE + 0x20)) // Some legacy status bits?


static bool bLgyIsSleeping = false;
static bool bLgyIsSleepingActual = false;
static bool bNeedsWakeCmd = false;

static void lgySleepIsr(u32 intSource)
{
if(intSource == IRQ_LGY_SLEEP)
{
// Workaround for The Legend of Zelda - A Link to the Past.
// This game doesn't set the IRQ enable bit so we force it
// on the 3DS side. Unknown if other games have this bug.
REG_HID_PADCNT = REG_LGY_PADCNT | 1u<<14;
bLgyIsSleeping = true;
}
else // IRQ_HID_PADCNT
{
// TODO: Synchronize with LCD VBlank.
REG_HID_PADCNT = 0;
REG_HID_PADCNT = REG_LGY_PADCNT & ~(1<<14);
REG_LGY_SLEEP |= 1u; // Acknowledge and wakeup.

bLgyIsSleeping = false;
}
}

Expand Down Expand Up @@ -139,10 +143,59 @@ Result LGY_backupGbaSave(void)
return PXI_sendCmd(IPC_CMD9_BACKUP_GBA_SAVE, NULL, 0);
}

int LGY_isSleeping(void)
{
return bLgyIsSleeping;
}

void LGY_deinit(void)
{
LGY_backupGbaSave();

IRQ_unregisterIsr(IRQ_LGY_SLEEP);
IRQ_unregisterIsr(IRQ_HID_PADCNT);
}

void LGY_sleepGba(void)
{
if (bLgyIsSleepingActual) return;

// Workaround for The Legend of Zelda - A Link to the Past.
// This game doesn't set the IRQ enable bit so we force it
// on the 3DS side. Unknown if other games have this bug.
REG_HID_PADCNT = REG_LGY_PADCNT | 1u<<14;

bNeedsWakeCmd = false;

// Only attempt to force GBA into sleep if we're not asleep
if (!bLgyIsSleeping)
{
bNeedsWakeCmd = true;
PXI_sendCmd(IPC_CMD9_SLEEPGBA, NULL, 0);
}

bLgyIsSleepingActual = true;
}

void LGY_wakeGba(void)
{
if (!bLgyIsSleepingActual) return;

// Only pull GBA out of forced sleep if sleep was forced
if (bNeedsWakeCmd)
{
//REG_HID_PADCNT = REG_LGY_PADCNT & ~(1<<14);
REG_LGY_SLEEP |= 1u; // Acknowledge and wakeup.

// Wait a frame just in case waking takes a bit
TIMER_sleepMs(17);

PXI_sendCmd(IPC_CMD9_WAKEGBA, NULL, 0);

// Wait a frame for ARM9 to finish waking
TIMER_sleepMs(17);
}

bNeedsWakeCmd = false;
bLgyIsSleepingActual = false;
}
41 changes: 41 additions & 0 deletions source/arm11/open_agb_firm.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "inih/ini.h"
#include "arm11/filebrowser.h"
#include "arm11/hardware/lcd.h"
#include "arm11/hardware/codec.h"
#include "arm11/gpu_cmd_lists.h"
#include "arm11/hardware/mcu.h"
#include "kernel.h"
Expand Down Expand Up @@ -466,6 +467,10 @@ static void gbaGfxHandler(void *args)
{
KEvent *const event = (KEvent*)args;

u32 curExtra, lastExtra;
lastExtra = 0;
bool curLgySleep, lastLgySleep;
lastLgySleep = false;
while(1)
{
if(waitForEvent(event) != KRES_OK) break;
Expand Down Expand Up @@ -496,6 +501,42 @@ static void gbaGfxHandler(void *args)
GFX_waitForPPF();
GFX_swapFramebufs();

// Scan input for KEY_SHELL and KEY_Y/KEY_SELECT
hidScanInput();

curLgySleep = LGY_isSleeping();
curExtra = hidGetExtraKeys(0);

// Check if shell was closed, or if GBA slept itself
if(((curExtra & KEY_SHELL) && !(lastExtra & KEY_SHELL))
|| (curLgySleep && !lastLgySleep))
{
// Turn off sound and screen preemptively to fake-sleep GBA
CODEC_muteI2S();
GFX_powerOffBacklights(GFX_BLIGHT_BOTH);

// Have ARM9 commandeer the ARM7 and force it into sleep
LGY_sleepGba();
}
else if((!(curExtra & KEY_SHELL) && (lastExtra & KEY_SHELL) && !curLgySleep)
|| (!curLgySleep && lastLgySleep))
{
// Restore backlight before waking.
GFX_powerOnBacklights(GFX_BLIGHT_BOTH);

// Force the GBA to wake up
LGY_wakeGba();

// Restore audio last, on the off chance that a DMA
// channel caused audio to glitch.
// Since the GBA is in real sleep mode,
// this should be mostly glitch-free.
CODEC_unmuteI2S();
}

lastExtra = curExtra;
lastLgySleep = curLgySleep;

if(hidKeysDown() == (KEY_Y | KEY_SELECT)) dumpFrameTex();
}

Expand Down
22 changes: 22 additions & 0 deletions source/arm9/arm7_stub.s
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,28 @@
.cpu arm7tdmi
.fpu softvfp

BEGIN_ASM_FUNC _a7_overlay_stub_capture
nop
nop

_swi_v:
b .-8
b .
b .
b .

@ I tried to have it so interrupts would finish before forced sleeps,
@ but it made some games crash so on the off chance we're mid-interrupt,
@ consume a bit more power.
b . @ .+0x110

_fiq_v:
b .

.pool
.global _a7_overlay_stub_capture_size
_a7_overlay_stub_capture_size = . - _a7_overlay_stub_capture
END_ASM_FUNC


BEGIN_ASM_FUNC _a7_overlay_stub
Expand Down
18 changes: 13 additions & 5 deletions source/arm9/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
#include "mem_map.h"
#include "hardware/pxi.h"
#include "ipc_handler.h"
//#include "fatfs/ff.h"
//#include "fs.h"
#include "fatfs/ff.h"
#include "fs.h"
#include "arm9/hardware/interrupt.h"
#include "hardware/gfx.h"
#include "arm9/hardware/ndma.h"


u8 test_buf[0x2000];


NOINLINE NOINLINE noreturn void panic(void)
{
Expand Down Expand Up @@ -74,15 +76,21 @@ NOINLINE noreturn void guruMeditation(UNUSED u8 type, UNUSED const u32 *excStack
}
}

/*void dumpMem(u8 *mem, u32 size, char *filepath)
void dumpMem(u8 *mem, u32 size, char *filepath)
{
FIL file;
UINT bytesWritten;

if(f_open(&file, filepath, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)
return;

f_write(&file, mem, size, &bytesWritten);
//f_write(&file, mem, size, &bytesWritten);
for (int i = 0; i < size; i += 0x1000)
{
NDMA_copy((u32*)&test_buf[0], (u32*)&mem[i], 0x1000);
f_write(&file, test_buf, 0x1000, &bytesWritten);
}

f_sync(&file);
f_close(&file);
}*/
}
Loading