// Copyright (C) 2016 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 of GPLv3 applies 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. // ============================================ // Screen init code by dark_samus, bil1s, Normmatt, delebile and others. // Screen deinit code by tiniVi. // More readable screeninit by Gelex. // ============================================ // This is less a permanent solution and more a quickfix since I'd rather use libctr11 // whenever Gelex finishes it. Don't rely on it staying here. // Also, this contains quite a few changes from luma's screen.c, which is where it originates from. #include #include #include #include #include struct framebuffers *framebuffers; volatile uint32_t *const arm11Entry = (volatile uint32_t *)0x1FFFFFF8; static const uint32_t brightness[4] = {0x26, 0x39, 0x4C, 0x5F}; void __attribute__((naked)) arm11Stub(void) { //Disable interrupts __asm(".word 0xF10C01C0"); //Wait for the entry to be set while(*arm11Entry == ARM11_STUB_ADDRESS); //Jump to it ((void (*)())*arm11Entry)(); } void installArm11Stub(void) { static int hasCopiedStub = false; if(!hasCopiedStub) { memcpy((void *)ARM11_STUB_ADDRESS, arm11Stub, 0x30); ctr_cache_clean_and_flush_all(); hasCopiedStub = true; } } void invokeArm11Function(void (*func)()) { installArm11Stub(); *arm11Entry = (uint32_t)func; while(*arm11Entry); *arm11Entry = ARM11_STUB_ADDRESS; } void deinitScreens(void) { void __attribute__((naked)) ARM11(void) { //Disable interrupts __asm(".word 0xF10C01C0"); //Shutdown LCDs *(volatile uint32_t *)0x10202A44 = 0; *(volatile uint32_t *)0x10202244 = 0; *(volatile uint32_t *)0x1020200C = 0; *(volatile uint32_t *)0x10202014 = 0; WAIT_FOR_ARM9(); } // If screen is initialized, invoke. if(PDN_GPU_CNT != 1) invokeArm11Function(ARM11); } void updateBrightness(uint32_t brightnessIndex) { static uint32_t brightnessLevel; brightnessLevel = brightness[brightnessIndex]; void __attribute__((naked)) ARM11(void) { //Disable interrupts __asm(".word 0xF10C01C0"); //Change brightness *(volatile uint32_t *)0x10202240 = brightnessLevel; *(volatile uint32_t *)0x10202A40 = brightnessLevel; WAIT_FOR_ARM9(); } ctr_cache_clean_and_flush_all(); invokeArm11Function(ARM11); } void clearScreens(void) { void __attribute__((naked)) ARM11(void) { //Disable interrupts __asm(".word 0xF10C01C0"); //Setting up two simultaneous memory fills using the GPU volatile uint32_t *REGs_PSC0 = (volatile uint32_t *)0x10400010; REGs_PSC0[0] = (uint32_t)framebuffers->top_left >> 3; //Start address REGs_PSC0[1] = (uint32_t)(framebuffers->top_left + (400 * 240 * 4)) >> 3; //End address REGs_PSC0[2] = 0; //Fill value REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start volatile uint32_t *REGs_PSC1 = (volatile uint32_t *)0x10400020; REGs_PSC1[0] = (uint32_t)framebuffers->bottom >> 3; //Start address REGs_PSC1[1] = (uint32_t)(framebuffers->bottom + (320 * 240 * 4)) >> 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))); WAIT_FOR_ARM9(); } ctr_cache_clean_and_flush_all(); invokeArm11Function(ARM11); } void screen_mode(uint32_t mode) { static uint32_t stride, init_top, init_bottom, bright; bright = brightness[config->options[OPTION_BRIGHTNESS]]; stride = 240; switch(mode) { case RGB8: stride *= 3; break; case RGBA8: stride *= 4; break; default: stride *= 2; break; } init_top = MAKE_FRAMEBUFFER_PIXFMT(mode, 0, 1); init_bottom = MAKE_FRAMEBUFFER_PIXFMT(mode, 0, 0); if (!framebuffers) { // Look ma, dynamically allocating the CakeHax struct! (joking) // We literally just discard the previous state - for sanity's sake. // On chainload, it is needed to copy the framebuffer struct. framebuffers = malloc(sizeof(struct framebuffers)); } void __attribute__((naked)) ARM11(void) { //Disable interrupts __asm(".word 0xF10C01C0"); PDN_GPU_CNT = 0x1007F; //bit0: Enable GPU regs 0x10400000+, bit16 turn on LCD backlight? LCD_REG(0x14) = 0x00000001; //UNKNOWN register, maybe LCD related? 0x10202000 LCD_REG(0xC) &= 0xFFFEFFFE; //UNKNOWN register, maybe LCD related? LCD_TOP_CONF_BRIGHTNESS = bright; LCD_BOT_CONF_BRIGHTNESS = bright; LCD_TOP_CONF_REG(0x44) = 0x1023E; //unknown LCD_BOT_CONF_REG(0x44) = 0x1023E; //unknown // Top screen PDC0_FRAMEBUFFER_SETUP_REG(0x00) = 0x000001c2; //unknown PDC0_FRAMEBUFFER_SETUP_REG(0x04) = 0x000000d1; //unknown PDC0_FRAMEBUFFER_SETUP_REG(0x08) = 0x000001c1; PDC0_FRAMEBUFFER_SETUP_REG(0x0c) = 0x000001c1; PDC0_FRAMEBUFFER_SETUP_REG(0x10) = 0x00000000; PDC0_FRAMEBUFFER_SETUP_REG(0x14) = 0x000000cf; PDC0_FRAMEBUFFER_SETUP_REG(0x18) = 0x000000d1; PDC0_FRAMEBUFFER_SETUP_REG(0x1c) = 0x01c501c1; PDC0_FRAMEBUFFER_SETUP_REG(0x20) = 0x00010000; PDC0_FRAMEBUFFER_SETUP_REG(0x24) = 0x0000019d; PDC0_FRAMEBUFFER_SETUP_REG(0x28) = 0x00000002; PDC0_FRAMEBUFFER_SETUP_REG(0x2c) = 0x00000192; PDC0_FRAMEBUFFER_SETUP_REG(0x30) = 0x00000192; PDC0_FRAMEBUFFER_SETUP_REG(0x34) = 0x00000192; PDC0_FRAMEBUFFER_SETUP_REG(0x38) = 0x00000001; PDC0_FRAMEBUFFER_SETUP_REG(0x3c) = 0x00000002; PDC0_FRAMEBUFFER_SETUP_REG(0x40) = 0x01960192; PDC0_FRAMEBUFFER_SETUP_REG(0x44) = 0x00000000; PDC0_FRAMEBUFFER_SETUP_REG(0x48) = 0x00000000; PDC0_FRAMEBUFFER_SETUP_DIMS = (240u << 16) | (400u); PDC0_FRAMEBUFFER_SETUP_REG(0x60) = 0x01c100d1; PDC0_FRAMEBUFFER_SETUP_REG(0x64) = 0x01920002; PDC0_FRAMEBUFFER_SETUP_FBA_ADDR_1 = 0x18300000; PDC0_FRAMEBUFFER_SETUP_FB_FORMAT = init_top; PDC0_FRAMEBUFFER_SETUP_REG(0x74) = 0x00010501; PDC0_FRAMEBUFFER_SETUP_FB_SELECT = 0; PDC0_FRAMEBUFFER_SETUP_FB_STRIDE = stride; PDC0_FRAMEBUFFER_SETUP_REG(0x9C) = 0x00000000; // Disco register for(volatile uint32_t i = 0; i < 256; i++) PDC0_FRAMEBUFFER_SETUP_DISCO = 0x10101 * i; // Bottom screen PDC1_FRAMEBUFFER_SETUP_REG(0x00) = 0x000001c2; PDC1_FRAMEBUFFER_SETUP_REG(0x04) = 0x000000d1; PDC1_FRAMEBUFFER_SETUP_REG(0x08) = 0x000001c1; PDC1_FRAMEBUFFER_SETUP_REG(0x0c) = 0x000001c1; PDC1_FRAMEBUFFER_SETUP_REG(0x10) = 0x000000cd; PDC1_FRAMEBUFFER_SETUP_REG(0x14) = 0x000000cf; PDC1_FRAMEBUFFER_SETUP_REG(0x18) = 0x000000d1; PDC1_FRAMEBUFFER_SETUP_REG(0x1c) = 0x01c501c1; PDC1_FRAMEBUFFER_SETUP_REG(0x20) = 0x00010000; PDC1_FRAMEBUFFER_SETUP_REG(0x24) = 0x0000019d; PDC1_FRAMEBUFFER_SETUP_REG(0x28) = 0x00000052; PDC1_FRAMEBUFFER_SETUP_REG(0x2c) = 0x00000192; PDC1_FRAMEBUFFER_SETUP_REG(0x30) = 0x00000192; PDC1_FRAMEBUFFER_SETUP_REG(0x34) = 0x0000004f; PDC1_FRAMEBUFFER_SETUP_REG(0x38) = 0x00000050; PDC1_FRAMEBUFFER_SETUP_REG(0x3c) = 0x00000052; PDC1_FRAMEBUFFER_SETUP_REG(0x40) = 0x01980194; PDC1_FRAMEBUFFER_SETUP_REG(0x44) = 0x00000000; PDC1_FRAMEBUFFER_SETUP_REG(0x48) = 0x00000011; PDC1_FRAMEBUFFER_SETUP_DIMS = (240u << 16) | 320u; PDC1_FRAMEBUFFER_SETUP_REG(0x60) = 0x01c100d1; PDC1_FRAMEBUFFER_SETUP_REG(0x64) = 0x01920052; PDC1_FRAMEBUFFER_SETUP_FBA_ADDR_1 = 0x1835dc00; PDC1_FRAMEBUFFER_SETUP_FB_FORMAT = init_bottom; PDC1_FRAMEBUFFER_SETUP_REG(0x74) = 0x00010501; PDC1_FRAMEBUFFER_SETUP_FB_SELECT = 0; PDC1_FRAMEBUFFER_SETUP_FB_STRIDE = stride; PDC1_FRAMEBUFFER_SETUP_REG(0x9C) = 0x00000000; // Disco register for(volatile uint32_t i = 0; i < 256; i++) PDC1_FRAMEBUFFER_SETUP_DISCO = 0x10101 * i; PDC0_FRAMEBUFFER_SETUP_FBA_ADDR_1 = 0x18300000; PDC0_FRAMEBUFFER_SETUP_FBA_ADDR_2 = 0x18300000; PDC0_FRAMEBUFFER_SETUP_FBB_ADDR_1 = 0x18300000; PDC0_FRAMEBUFFER_SETUP_FBB_ADDR_2 = 0x18300000; PDC1_FRAMEBUFFER_SETUP_FBA_ADDR_1 = 0x1835dc00; PDC1_FRAMEBUFFER_SETUP_FBA_ADDR_2 = 0x1835dc00; // Set not-actually cakebrah framebuffers. Meh. framebuffers->top_left = (uint8_t *)0x18300000; framebuffers->top_right = (uint8_t *)0x18300000; framebuffers->bottom = (uint8_t *)0x1835dc00; WAIT_FOR_ARM9(); } ctr_cache_clean_and_flush_all(); invokeArm11Function(ARM11); clearScreens(); // Turn on backlight // i2cWriteRegister(I2C_DEV_MCU, 0x22, 1 << 1); //Turn on backlight i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); }