diff --git a/fusee/fusee-primary/src/hwinit.c b/fusee/fusee-primary/src/hwinit.c index 47d5ecf27..de4df1954 100644 --- a/fusee/fusee-primary/src/hwinit.c +++ b/fusee/fusee-primary/src/hwinit.c @@ -194,7 +194,10 @@ void nx_hwinit() AHB_AHB_SPARE_REG_0 &= 0xFFFFFF9F; pmc->scratch49 = (((pmc->scratch49 >> 1) << 1) & 0xFFFFFFFD); + /* Apply the memory built-in self test workaround. */ mbist_workaround(); + + /* Reboot SE. */ clkrst_reboot(CARDEVICE_SE); /* Initialize the fuse driver. */ @@ -203,9 +206,15 @@ void nx_hwinit() /* Initialize the memory controller. */ mc_enable(); - /* Configure oscillators, pinmux and GPIOs. */ + /* Configure oscillators. */ config_oscillators(); + + /* Disable pinmux tristate input clamping. */ APB_MISC_PP_PINMUX_GLOBAL_0 = 0; + + /* Configure GPIOs. */ + /* NOTE: In 3.x+ part of the GPIO configuration is skipped if the unit is SDEV. */ + /* NOTE: In 6.x+ the GPIO configuration's order was changed a bit. */ config_gpios(); /* Uncomment for UART debugging. */ @@ -214,21 +223,34 @@ void nx_hwinit() uart_init(UART_C, 115200); */ + /* Reboot CL-DVFS. */ clkrst_reboot(CARDEVICE_CL_DVFS); + + /* Reboot I2C1. */ clkrst_reboot(CARDEVICE_I2C1); + + /* Reboot I2C5. */ clkrst_reboot(CARDEVICE_I2C5); + + /* Reboot SE. */ + /* NOTE: In 4.x+ this was removed. */ clkrst_reboot(CARDEVICE_SE); + + /* Reboot unknown device. */ clkrst_reboot(CARDEVICE_UNK); - /* Initialize I2C1 and I2C5. */ + /* Initialize I2C1. */ + /* NOTE: In 6.x+ this was moved to after the PMIC is configured. */ i2c_init(I2C_1); + + /* Initialize I2C5. */ i2c_init(I2C_5); + /* Configure the PMIC. */ uint8_t val = 0x40; i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGBBC, &val, 1); val = 0x78; i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, &val, 1); - val = 0x38; i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_CFG0, &val, 1); val = 0x3A; @@ -245,19 +267,38 @@ void nx_hwinit() i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_SD1, &val, 1); val = 0x1B; i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_SD3, &val, 1); + + /* TODO: In 3.x+ this was added. */ + /* + val = 0x22; + i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_GPIO3, &val, 1); + */ + + /* TODO: In 3.x+, if the unit is SDEV, the MBLPD bit is set. */ + /* + i2c_query(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGGLBL1, &val, 1); + val |= 0x40; + i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGGLBL1, &val, 1); + */ + /* Configure SD0 voltage. */ val = 42; /* 42 = (1125000 - 600000) / 12500 -> 1.125V */ i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_SD0, &val, 1); /* Configure and lock PMC scratch registers. */ + /* NOTE: In 4.x+ this was removed. */ config_pmc_scratch(); + /* Set super clock burst policy. */ car->sclk_brst_pol = ((car->sclk_brst_pol & 0xFFFF8888) | 0x3333); /* Configure memory controller carveouts. */ + /* NOTE: In 4.x+ this was removed. */ mc_config_carveout(); - /* Initialize and save SDRAM. */ + /* Initialize SDRAM. */ sdram_init(); + + /* Save SDRAM LP0 parameters. */ sdram_lp0_save_params(sdram_get_params()); } \ No newline at end of file diff --git a/fusee/fusee-primary/src/lib/lz.c b/fusee/fusee-primary/src/lib/lz.c index a17c6e49f..564d480ad 100644 --- a/fusee/fusee-primary/src/lib/lz.c +++ b/fusee/fusee-primary/src/lib/lz.c @@ -78,11 +78,73 @@ *************************************************************************/ +/************************************************************************* +* Constants used for LZ77 coding +*************************************************************************/ + +/* Maximum offset (can be any size < 2^31). Lower values give faster + compression, while higher values gives better compression. The default + value of 100000 is quite high. Experiment to see what works best for + you. */ +#define LZ_MAX_OFFSET 100000 + + + /************************************************************************* * INTERNAL FUNCTIONS * *************************************************************************/ +/************************************************************************* +* _LZ_StringCompare() - Return maximum length string match. +*************************************************************************/ + +static unsigned int _LZ_StringCompare( unsigned char * str1, + unsigned char * str2, unsigned int minlen, unsigned int maxlen ) +{ + unsigned int len; + + for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len ); + + return len; +} + + +/************************************************************************* +* _LZ_WriteVarSize() - Write unsigned integer with variable number of +* bytes depending on value. +*************************************************************************/ + +static int _LZ_WriteVarSize( unsigned int x, unsigned char * buf ) +{ + unsigned int y; + int num_bytes, i, b; + + /* Determine number of bytes needed to store the number x */ + y = x >> 3; + for( num_bytes = 5; num_bytes >= 2; -- num_bytes ) + { + if( y & 0xfe000000 ) break; + y <<= 7; + } + + /* Write all bytes, seven bits in each, with 8:th bit set for all */ + /* but the last byte. */ + for( i = num_bytes-1; i >= 0; -- i ) + { + b = (x >> (i*7)) & 0x0000007f; + if( i > 0 ) + { + b |= 0x00000080; + } + *buf ++ = (unsigned char) b; + } + + /* Return number of bytes written */ + return num_bytes; +} + + /************************************************************************* * _LZ_ReadVarSize() - Read unsigned integer with variable number of * bytes depending on value. @@ -117,6 +179,141 @@ static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf ) *************************************************************************/ +/************************************************************************* +* LZ_Compress() - Compress a block of data using an LZ77 coder. +* in - Input (uncompressed) buffer. +* out - Output (compressed) buffer. This buffer must be 0.4% larger +* than the input buffer, plus one byte. +* insize - Number of input bytes. +* The function returns the size of the compressed data. +*************************************************************************/ + +int LZ_Compress( const unsigned char *in, unsigned char *out, unsigned int insize ) +{ + unsigned char marker, symbol; + unsigned int inpos, outpos, bytesleft, i; + unsigned int maxoffset, offset, bestoffset; + unsigned int maxlength, length, bestlength; + unsigned int histogram[ 256 ]; + unsigned char *ptr1, *ptr2; + + /* Do we have anything to compress? */ + if( insize < 1 ) + { + return 0; + } + + /* Create histogram */ + for( i = 0; i < 256; ++ i ) + { + histogram[ i ] = 0; + } + for( i = 0; i < insize; ++ i ) + { + ++ histogram[ in[ i ] ]; + } + + /* Find the least common byte, and use it as the marker symbol */ + marker = 0; + for( i = 1; i < 256; ++ i ) + { + if( histogram[ i ] < histogram[ marker ] ) + { + marker = (unsigned char) i; + } + } + + /* Remember the marker symbol for the decoder */ + out[ 0 ] = marker; + + /* Start of compression */ + inpos = 0; + outpos = 1; + + /* Main compression loop */ + bytesleft = insize; + do + { + /* Determine most distant position */ + if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET; + else maxoffset = inpos; + + /* Get pointer to current position */ + ptr1 = &in[ inpos ]; + + /* Search history window for maximum length string match */ + bestlength = 3; + bestoffset = 0; + for( offset = 3; offset <= maxoffset; ++ offset ) + { + /* Get pointer to candidate string */ + ptr2 = &ptr1[ -(int)offset ]; + + /* Quickly determine if this is a candidate (for speed) */ + if( (ptr1[ 0 ] == ptr2[ 0 ]) && + (ptr1[ bestlength ] == ptr2[ bestlength ]) ) + { + /* Determine maximum length for this offset */ + maxlength = (bytesleft < offset ? bytesleft : offset); + + /* Count maximum length match at this offset */ + length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength ); + + /* Better match than any previous match? */ + if( length > bestlength ) + { + bestlength = length; + bestoffset = offset; + } + } + } + + /* Was there a good enough match? */ + if( (bestlength >= 8) || + ((bestlength == 4) && (bestoffset <= 0x0000007f)) || + ((bestlength == 5) && (bestoffset <= 0x00003fff)) || + ((bestlength == 6) && (bestoffset <= 0x001fffff)) || + ((bestlength == 7) && (bestoffset <= 0x0fffffff)) ) + { + out[ outpos ++ ] = (unsigned char) marker; + outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] ); + outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] ); + inpos += bestlength; + bytesleft -= bestlength; + } + else + { + /* Output single byte (or two bytes if marker byte) */ + symbol = in[ inpos ++ ]; + out[ outpos ++ ] = symbol; + if( symbol == marker ) + { + out[ outpos ++ ] = 0; + } + -- bytesleft; + } + } + while( bytesleft > 3 ); + + /* Dump remaining bytes, if any */ + while( inpos < insize ) + { + if( in[ inpos ] == marker ) + { + out[ outpos ++ ] = marker; + out[ outpos ++ ] = 0; + } + else + { + out[ outpos ++ ] = in[ inpos ]; + } + ++ inpos; + } + + return outpos; +} + + /************************************************************************* * LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder. * in - Input (compressed) buffer. @@ -125,8 +322,7 @@ static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf ) * insize - Number of input bytes. *************************************************************************/ -void LZ_Uncompress( const unsigned char *in, unsigned char *out, - unsigned int insize ) +int LZ_Uncompress( const unsigned char *in, unsigned char *out, unsigned int insize ) { unsigned char marker, symbol; unsigned int i, inpos, outpos, length, offset; @@ -134,7 +330,7 @@ void LZ_Uncompress( const unsigned char *in, unsigned char *out, /* Do we have anything to uncompress? */ if( insize < 1 ) { - return; + return 0; } /* Get marker symbol from input stream */ @@ -176,4 +372,6 @@ void LZ_Uncompress( const unsigned char *in, unsigned char *out, } } while( inpos < insize ); + + return outpos; } diff --git a/fusee/fusee-primary/src/lib/lz.h b/fusee/fusee-primary/src/lib/lz.h index 6f31b4a14..bc40b6c63 100644 --- a/fusee/fusee-primary/src/lib/lz.h +++ b/fusee/fusee-primary/src/lib/lz.h @@ -41,9 +41,8 @@ extern "C" { * Function prototypes *************************************************************************/ -void LZ_Uncompress( const unsigned char *in, unsigned char *out, - unsigned int insize ); - +int LZ_Compress(const unsigned char *in, unsigned char *out, unsigned int insize); +int LZ_Uncompress(const unsigned char *in, unsigned char *out, unsigned int insize); #ifdef __cplusplus } diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index bad10f017..55377889f 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -80,7 +80,7 @@ CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) KIPFILES := loader.kip pm.kip sm.kip boot_100.kip boot_200.kip -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) exosphere.bin thermosphere.bin $(KIPFILES) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) exosphere.bin thermosphere.bin splash_screen.bmp $(KIPFILES) #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C @@ -166,6 +166,11 @@ $(OFILES_SRC) : $(HFILES_BIN) #--------------------------------------------------------------------------------- @echo $(notdir $<) @$(bin2o) + +%.bmp.o %_bmp.h: %.bmp +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) %.kip.o %_kip.h: %.kip #--------------------------------------------------------------------------------- diff --git a/fusee/fusee-secondary/src/exocfg.h b/fusee/fusee-secondary/src/exocfg.h index dedc059fe..2b3417629 100644 --- a/fusee/fusee-secondary/src/exocfg.h +++ b/fusee/fusee-secondary/src/exocfg.h @@ -27,9 +27,10 @@ #define EXOSPHERE_TARGET_FIRMWARE_300 3 #define EXOSPHERE_TARGET_FIRMWARE_400 4 #define EXOSPHERE_TARGET_FIRMWARE_500 5 +#define EXOSPHERE_TARGET_FIRMWARE_600 6 #define EXOSPHERE_TARGET_FIRMWARE_MIN EXOSPHERE_TARGET_FIRMWARE_100 -#define EXOSPHERE_TARGET_FIRMWARE_MAX EXOSPHERE_TARGET_FIRMWARE_500 +#define EXOSPHERE_TARGET_FIRMWARE_MAX EXOSPHERE_TARGET_FIRMWARE_600 typedef struct { unsigned int magic; diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 6f91206ab..68396210b 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -75,6 +75,8 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) { return EXOSPHERE_TARGET_FIRMWARE_400; case 0x0B: /* 5.0.0 - 5.1.0 */ return EXOSPHERE_TARGET_FIRMWARE_500; + case 0x0E: /* 6.0.0 */ + return EXOSPHERE_TARGET_FIRMWARE_600; default: return 0; } @@ -158,6 +160,8 @@ static void nxboot_set_bootreason() { static void nxboot_move_bootconfig() { FILE *bcfile; void *bootconfig; + uint32_t bootconfig_addr; + uint32_t bootconfig_size; /* Allocate memory for reading BootConfig. */ bootconfig = memalign(0x1000, 0x4000); @@ -175,9 +179,13 @@ static void nxboot_move_bootconfig() { } fclose(bcfile); - /* Copy the first 0x3000 bytes into IRAM. */ - memset((void *)0x4003D000, 0, 0x3000); - memcpy((void *)0x4003D000, bootconfig, 0x3000); + /* Select the actual BootConfig size and destination address. */ + bootconfig_addr = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_600) ? 0x4003D000 : 0x4003F800; + bootconfig_size = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_400) ? 0x3000 : 0x1000; + + /* Copy the BootConfig into IRAM. */ + memset((void *)bootconfig_addr, 0, bootconfig_size); + memcpy((void *)bootconfig_addr, bootconfig, bootconfig_size); /* Clean up. */ free(bootconfig); @@ -329,8 +337,10 @@ uint32_t nxboot_main(void) { /* Select the right address for the warmboot firmware. */ if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_400) { warmboot_memaddr = (void *)0x8000D000; - } else { + } else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_600) { warmboot_memaddr = (void *)0x4003B000; + } else { + warmboot_memaddr = (void *)0x4003D800; } printf("[NXBOOT]: Copying warmboot firmware...\n"); @@ -399,7 +409,7 @@ uint32_t nxboot_main(void) { printf("[NXBOOT]: Powering on the CCPLEX...\n"); /* Display splash screen. */ - display_splash_screen_bmp(loader_ctx->custom_splash_path); + display_splash_screen_bmp(loader_ctx->custom_splash_path, (void *)0xC0000000); /* Unmount everything. */ nxfs_unmount_all(); diff --git a/fusee/fusee-secondary/src/splash_screen.bmp b/fusee/fusee-secondary/src/splash_screen.bmp new file mode 100644 index 000000000..3fe43bd90 Binary files /dev/null and b/fusee/fusee-secondary/src/splash_screen.bmp differ diff --git a/fusee/fusee-secondary/src/splash_screen.c b/fusee/fusee-secondary/src/splash_screen.c index 172516c90..97b258ccd 100644 --- a/fusee/fusee-secondary/src/splash_screen.c +++ b/fusee/fusee-secondary/src/splash_screen.c @@ -15,20 +15,75 @@ */ #include +#include "di.h" #include "timers.h" #include "splash_screen.h" #include "fs_utils.h" #include "display/video_fb.h" -void display_splash_screen_bmp(const char *custom_splash_path) { - uint8_t *splash_screen = g_default_splash_screen; +#define u8 uint8_t +#define u32 uint32_t +#include "splash_screen_bmp.h" +#undef u8 +#undef u32 + +static void render_bmp(const uint32_t *bmp_data, uint32_t *framebuffer, uint32_t bmp_width, uint32_t bmp_height, uint32_t bmp_pos_x, uint32_t bmp_pos_y) { + /* Render the BMP. */ + for (uint32_t y = bmp_pos_y; y < (bmp_pos_y + bmp_height); y++) { + for (uint32_t x = bmp_pos_x; x < (bmp_pos_x + bmp_width); x++) { + framebuffer[x + (y * SPLASH_SCREEN_STRIDE)] = bmp_data[(bmp_height + bmp_pos_y - 1 - y) * bmp_width + x - bmp_pos_x]; + } + } + + /* Re-initialize the frame buffer. */ + display_init_framebuffer(framebuffer); +} + +void display_splash_screen_bmp(const char *custom_splash_path, void *fb_address) { + uint8_t *splash_screen = (uint8_t *)splash_screen_bmp; + + /* Try to load an external custom splash screen. */ if ((custom_splash_path != NULL) && (custom_splash_path[0] != '\x00')) { - if (!read_from_file(splash_screen, sizeof(g_default_splash_screen), custom_splash_path)) { + if (!read_from_file(splash_screen, sizeof(&splash_screen_bmp), custom_splash_path)) { fatal_error("Failed to read custom splash screen from %s!\n", custom_splash_path); } } - - /* TODO: Display the splash screen. It should be a pointer to a BMP, at this point. */ + + /* Check for 'BM' magic. */ + if ((splash_screen[0] == 'B') && (splash_screen[1] == 'M')) { + /* Extract BMP parameters. */ + uint32_t bmp_size = (splash_screen[0x02] | (splash_screen[0x03] << 8) | (splash_screen[0x04] << 16) | (splash_screen[0x05] << 24)); + uint32_t bmp_offset = (splash_screen[0x0A] | (splash_screen[0x0B] << 8) | (splash_screen[0x0C] << 16) | (splash_screen[0x0D] << 24)); + uint32_t bmp_width = (splash_screen[0x12] | (splash_screen[0x13] << 8) | (splash_screen[0x14] << 16) | (splash_screen[0x15] << 24)); + uint32_t bmp_height = (splash_screen[0x16] | (splash_screen[0x17] << 8) | (splash_screen[0x18] << 16) | (splash_screen[0x19] << 24)); + uint16_t bmp_bpp = (splash_screen[0x1C] | (splash_screen[0x1D] << 8)); + uint32_t bmp_data_size = (splash_screen[0x22] | (splash_screen[0x23] << 8) | (splash_screen[0x24] << 16) | (splash_screen[0x25] << 24)); + + /* Data size can be wrong or set to 0. In that case, we calculate it instead. */ + if (!bmp_data_size || (bmp_data_size >= bmp_size)) + bmp_data_size = (bmp_size - bmp_offset); + + /* Only accept images up to 720x1280 resolution and with 32 BPP. */ + if ((bmp_width > SPLASH_SCREEN_WIDTH_MAX) || (bmp_height > SPLASH_SCREEN_HEIGHT_MAX)) { + fatal_error("Invalid splash screen dimensions!\n"); + } else if (bmp_bpp != SPLASH_SCREEN_BPP) { + fatal_error("Invalid splash screen color depth!\n"); + } else if (bmp_data_size > SPLASH_SCREEN_SIZE_MAX) { + fatal_error("Splash screen data size is too big!\n"); + } + + /* Calculate screen positions. */ + uint32_t bmp_pos_x = ((SPLASH_SCREEN_WIDTH_MAX - bmp_width) / 2); + uint32_t bmp_pos_y = ((SPLASH_SCREEN_HEIGHT_MAX - bmp_height) / 2); + + /* Advance to data. */ + splash_screen += bmp_offset; + + /* Render the BMP. */ + render_bmp((uint32_t *)splash_screen, (uint32_t *)fb_address, bmp_width, bmp_height, bmp_pos_x, bmp_pos_y); + } else { + fatal_error("Invalid splash screen format!\n"); + } /* Display the splash screen for three seconds. */ udelay(3000000); diff --git a/fusee/fusee-secondary/src/splash_screen.h b/fusee/fusee-secondary/src/splash_screen.h index 032337744..0cca4fe52 100644 --- a/fusee/fusee-secondary/src/splash_screen.h +++ b/fusee/fusee-secondary/src/splash_screen.h @@ -19,9 +19,12 @@ #include -/* TODO: Actually make this a real thing. */ -extern unsigned char g_default_splash_screen[1]; +#define SPLASH_SCREEN_WIDTH_MAX 720 +#define SPLASH_SCREEN_HEIGHT_MAX 1280 +#define SPLASH_SCREEN_BPP 32 +#define SPLASH_SCREEN_STRIDE 768 +#define SPLASH_SCREEN_SIZE_MAX (SPLASH_SCREEN_HEIGHT_MAX * SPLASH_SCREEN_STRIDE * 4) -void display_splash_screen_bmp(const char *custom_splash_path); +void display_splash_screen_bmp(const char *custom_splash_path, void *fb_address); #endif diff --git a/fusee/fusee-secondary/src/splash_screen_default.c b/fusee/fusee-secondary/src/splash_screen_default.c deleted file mode 100644 index c1a955d5f..000000000 --- a/fusee/fusee-secondary/src/splash_screen_default.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2018 Atmosphère-NX - * - * 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 . - */ - -#include "splash_screen.h" - -uint8_t g_default_splash_screen[1] = {0};