Index: firmware/export/config-ipodvideo.h =================================================================== --- firmware/export/config-ipodvideo.h (revision 19970) +++ firmware/export/config-ipodvideo.h (working copy) @@ -204,4 +204,13 @@ #define IPOD_ACCESSORY_PROTOCOL #define HAVE_SERIAL +#ifndef BOOTLOADER +/* Support for LCD sleep/BCM shutdown */ +#define HAVE_LCD_ENABLE +#define HAVE_LCD_SLEEP +#define HAVE_LCD_SLEEP_SETTING +/* The same code may also be used when shutting down the iPod */ +#define HAVE_LCD_SHUTDOWN #endif + +#endif Index: firmware/backlight.c =================================================================== --- firmware/backlight.c (revision 19970) +++ firmware/backlight.c (working copy) @@ -219,8 +219,7 @@ /* Start LCD sleep countdown */ if (lcd_sleep_timeout < 0) { - lcd_sleep_timer = 0; /* Setting == Always */ - lcd_sleep(); + lcd_sleep_timer = 1; /* Setting == Always, sleep on next tick */ } else { @@ -362,6 +361,9 @@ #ifdef HAVE_LCD_SLEEP backlight_lcd_sleep_countdown(false); #endif +#ifdef HAVE_LCD_ENABLE + lcd_enable(true); /* power on lcd + visible display */ +#endif if (bl_fade_in_step > 0) { @@ -802,14 +804,14 @@ void backlight_set_timeout(int value) { backlight_timeout_normal = HZ * value; - backlight_update_state(); + backlight_on(); } #if CONFIG_CHARGING void backlight_set_timeout_plugged(int value) { backlight_timeout_plugged = HZ * value; - backlight_update_state(); + backlight_on(); } #endif /* CONFIG_CHARGING */ @@ -829,7 +831,7 @@ index = 0; backlight_on_button_hold = index; - backlight_update_state(); + backlight_on(); } #endif /* HAS_BUTTON_HOLD */ Index: firmware/target/arm/ipod/video/lcd-video.c =================================================================== --- firmware/target/arm/ipod/video/lcd-video.c (revision 19970) +++ firmware/target/arm/ipod/video/lcd-video.c (working copy) @@ -50,12 +50,34 @@ #define BCM_ALT_RD_ADDR32 (*(volatile unsigned long *)(0x30060000)) #define BCM_ALT_CONTROL (*(volatile unsigned short*)(0x30070000)) -#define BCM_FB_BASE 0xE0020 /* Address of internal BCM framebuffer */ - /* Time until the BCM is considered stalled and will be re-kicked. * Must be guaranteed to be >~ 20ms. */ #define BCM_UPDATE_TIMEOUT (HZ/20) +/* Addresses within BCM */ +#define BCMA_SRAM_BASE 0 +#define BCMA_COMMAND 0x1F8 +#define BCMA_STATUS 0x1FC +/* Command parameters, except for TV commands */ +#define BCMA_CMDPARAM 0xE0000 + +/* BCM commands. Write them to BCMA_COMMAND. Note BCM_CMD encoding. */ +#define BCM_CMD(x) ((~((unsigned long)x) << 16) | ((unsigned long)x)) +#define BCMCMD_LCD_UPDATE BCM_CMD(0) +/* Execute "M25 Diagnostics". Status displayed on LCD. Takes <40s */ +#define BCMCMD_SELFTEST BCM_CMD(1) +#define BCMCMD_TV_PALBMP BCM_CMD(2) +#define BCMCMD_TV_NTSCBMP BCM_CMD(3) +/* BCM_CMD(4) may be another TV-related command */ +/* The following might do more depending on word at 0xE00000 */ +#define BCMCMD_LCD_UPDATERECT BCM_CMD(5) +#define BCMCMD_LCD_SLEEP BCM_CMD(8) +/* BCM_CMD(12) involved in shutdown */ +/* Macrovision analog copy prevention is on by default on TV output. + Execute this command after enabling TV out to turn it off. + */ +#define BCMCMD_TV_MVOFF BCM_CMD(14) + enum lcd_status { LCD_IDLE, LCD_INITIAL, @@ -70,9 +92,57 @@ #if NUM_CORES > 1 struct corelock cl; /* inter-core sync */ #endif +#ifdef HAVE_LCD_ENABLE + bool display_on; +#endif } lcd_state IBSS_ATTR; +#ifdef HAVE_LCD_ENABLE +static long bcm_off_wait; +const fb_data *flash_vmcs_offset; +unsigned flash_vmcs_length; +#define ROM_BASE 0x20000000 +#define ROM_ID(a,b,c,d) (unsigned int)( ((unsigned int)(d)) | \ + (((unsigned int)(c)) << 8) | \ + (((unsigned int)(b)) << 16) | \ + (((unsigned int)(a)) << 24) ) + +/* Get address and length of iPod flash section. + Based on part of FS#6721. This may belong elsewhere. + (BCM initialization needs vmcs section) + */ +bool flash_get_section(const unsigned int imageid, + void **offset, + unsigned int *length) +{ + unsigned long *p = (unsigned long*)(ROM_BASE + 0xffe00); + unsigned char *csp, *csend; + unsigned long checksum; + + /* Find the image in the directory */ + while (1) { + if (p[0] != ROM_ID('f','l','s','h')) + return false; + if (p[1] == imageid) + break; + p += 10; + } + + *offset = (void *)(p[3]); + *length = p[4]; + + /* Verify checksum. Probably unnecessary, but it's fast. */ + checksum = 0; + csend = (unsigned char *)(ROM_BASE + p[3] + p[4]); + for(csp = (unsigned char *)(ROM_BASE + p[3]); csp < csend; csp++) { + checksum += *csp; + } + + return checksum == p[7]; +} +#endif /* HAVE_LCD_ENABLE */ + static inline void bcm_write_addr(unsigned address) { BCM_WR_ADDR32 = address; /* write destination address */ @@ -99,16 +169,6 @@ return BCM_DATA32; /* read value */ } -static void bcm_setup_rect(unsigned x, unsigned y, - unsigned width, unsigned height) -{ - bcm_write_addr(0xE0004); - BCM_DATA32 = x; - BCM_DATA32 = y; - BCM_DATA32 = x + width - 1; - BCM_DATA32 = y + height - 1; -} - #ifndef BOOTLOADER static void lcd_tick(void) { @@ -119,15 +179,15 @@ if (!lcd_state.blocked && lcd_state.state >= LCD_NEED_UPDATE) { - unsigned data = bcm_read32(0x1F8); - bool bcm_is_busy = (data == 0xFFFA0005 || data == 0xFFFF); + unsigned data = bcm_read32(BCMA_COMMAND); + bool bcm_is_busy = (data == BCMCMD_LCD_UPDATE || data == 0xFFFF); if (((lcd_state.state == LCD_NEED_UPDATE) && !bcm_is_busy) /* Update requested and BCM is no longer busy. */ || (TIME_AFTER(current_tick, lcd_state.update_timeout) && bcm_is_busy)) /* BCM still busy after timeout, i.e. stalled. */ { - bcm_write32(0x1F8, 0xFFFA0005); /* Kick off update */ + bcm_write32(BCMA_COMMAND, BCMCMD_LCD_UPDATE); /* Kick off update */ BCM_CONTROL = 0x31; lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT; lcd_state.state = LCD_UPDATING; @@ -166,13 +226,13 @@ #if NUM_CORES > 1 corelock_lock(&lcd_state.cl); #endif - data = bcm_read32(0x1F8); - bcm_is_busy = (data == 0xFFFA0005 || data == 0xFFFF); + data = bcm_read32(BCMA_COMMAND); + bcm_is_busy = (data == BCMCMD_LCD_UPDATE || data == 0xFFFF); if (!bcm_is_busy || (lcd_state.state == LCD_INITIAL) || TIME_AFTER(current_tick, lcd_state.update_timeout)) { - bcm_write32(0x1F8, 0xFFFA0005); /* Kick off update */ + bcm_write32(BCMA_COMMAND, BCMCMD_LCD_UPDATE); /* Kick off update */ BCM_CONTROL = 0x31; lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT; lcd_state.state = LCD_UPDATING; @@ -199,14 +259,14 @@ if (lcd_state.state != LCD_INITIAL) { - data = bcm_read32(0x1F8); - while (data == 0xFFFA0005 || data == 0xFFFF) + data = bcm_read32(BCMA_COMMAND); + while (data == BCMCMD_LCD_UPDATE || data == 0xFFFF) { yield(); - data = bcm_read32(0x1F8); + data = bcm_read32(BCMA_COMMAND); } } - bcm_write32(0x1F8, 0xFFFA0005); /* Kick off update */ + bcm_write32(BCMA_COMMAND, BCMCMD_LCD_UPDATE); /* Kick off update */ BCM_CONTROL = 0x31; lcd_state.state = LCD_IDLE; } @@ -236,13 +296,18 @@ /* LCD init */ void lcd_init_device(void) { - bcm_setup_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); lcd_state.blocked = false; lcd_state.state = LCD_INITIAL; + lcd_state.display_on = true; /* Code in flash turned it on */ #ifndef BOOTLOADER #if NUM_CORES > 1 corelock_init(&lcd_state.cl); #endif +#ifdef HAVE_LCD_ENABLE + if (!flash_get_section(ROM_ID('v', 'm', 'c', 's'), + (void **)(&flash_vmcs_offset), &flash_vmcs_length)) + flash_vmcs_length = 0; +#endif tick_add_task(&lcd_tick); #endif /* !BOOTLOADER */ } @@ -255,6 +320,9 @@ const fb_data *addr; unsigned bcmaddr; + if (!lcd_state.display_on) + return; + if (x + width >= LCD_WIDTH) width = LCD_WIDTH - x; if (y + height >= LCD_HEIGHT) @@ -272,7 +340,7 @@ lcd_block_tick(); addr = &lcd_framebuffer[y][x]; - bcmaddr = BCM_FB_BASE + (LCD_WIDTH*2) * y + (x << 1); + bcmaddr = BCMA_CMDPARAM + (LCD_WIDTH*2) * y + (x << 1); if (width == LCD_WIDTH) { @@ -315,6 +383,9 @@ off_t z; unsigned char const * yuv_src[3]; + if (!lcd_state.display_on) + return; + /* Sorry, but width and height must be >= 2 or else */ width &= ~1; @@ -326,7 +397,7 @@ /* Prevent the tick from triggering BCM updates while we're writing. */ lcd_block_tick(); - bcmaddr = BCM_FB_BASE + (LCD_WIDTH*2) * y + (x << 1); + bcmaddr = BCMA_CMDPARAM + (LCD_WIDTH*2) * y + (x << 1); height >>= 1; do @@ -341,3 +412,180 @@ lcd_unblock_and_update(); } + +#ifdef HAVE_LCD_ENABLE +/* Executes a BCM command immediately and waits for it to complete. + Other BCM commands (eg. LCD updates or lcd_tick) must not interfere. + */ +static void bcm_command(unsigned cmd) { + unsigned status; + + bcm_write32(BCMA_COMMAND, cmd); + + BCM_CONTROL = 0x31; + + while (1) { + status = bcm_read32(BCMA_COMMAND); + if (status != cmd && status != 0xFFFF) + break; + yield(); + } +} + +void bcm_powerdown(void) +{ + bcm_write32(0x10001400, bcm_read32(0x10001400) & ~0xF0); + + /* Blanks the LCD and decreases power consumption + below what clearing the LCD would achieve. + Executing an LCD update command wakes it. + */ + bcm_command(BCMCMD_LCD_SLEEP); + + /* Not sure if this does anything */ + bcm_command(BCM_CMD(0xC)); + + /* Further cuts power use, probably by powering down BCM. + After this point, BCM needs to be bootstrapped + */ + GPO32_ENABLE |= 0x4000; + GPO32_VAL &= ~0x4000; + + GPO32_ENABLE |= 0x8000; + GPO32_VAL &= ~0x8000; +} + +/* Data written to BCM_CONTROL and BCM_ALT_CONTROL */ +const unsigned char bcm_bootstrapdata[] = { + 0xA1, 0x81, 0x91, 0x02, 0x12, 0x22, 0x72, 0x62 +}; + +void bcm_init(void) +{ + int i; + + /* Power up BCM */ + GPO32_ENABLE |= 0x4000; + GPO32_VAL |= 0x4000; + + GPO32_ENABLE |= 0x8000; + GPO32_VAL &= ~0x8000; + + /* Changed from HZ/2 to speed up lcd_enable(true) */ + sleep(HZ/8); + + /* Bootstrap stage 1 */ + + STRAP_OPT_A &= ~0xF00; + outl(0x1313, 0x70000040); + outl(0x8000, 0x6000D808); + + outl(inl(0x6000D008) | 0x40, 0x6000D008); + outl(inl(0x6000D018) & ~0x40, 0x6000D018); + outl(inl(0x6000D068) | 0x40, 0x6000D068); + outl(inl(0x6000D058) | 0x40, 0x6000D058); + + GPO32_ENABLE &= ~1; + + /* For BCM interrupts: CPU_HI_INT_EN |= 0x40000; */ + + /* Bootstrap stage 2 */ + + for (i = 0; i < 8; i++) { + BCM_CONTROL = bcm_bootstrapdata[i]; + } + + for (i = 3; i < 8; i++) { + BCM_ALT_CONTROL = bcm_bootstrapdata[i]; + } + + while ((BCM_RD_ADDR & 1) == 0 || (BCM_ALT_RD_ADDR & 1) == 0) + yield(); + + (void)BCM_WR_ADDR; + (void)BCM_ALT_WR_ADDR; + + /* Bootstrap stage 3: upload firmware */ + + while (BCM_ALT_CONTROL & 0x80) + yield(); + + while (!(BCM_ALT_CONTROL & 0x40)) + yield(); + + /* Upload firmware to BCM SRAM */ + bcm_write_addr(BCMA_SRAM_BASE); + lcd_write_data((const fb_data*)(0x20000000+0xA1F44), 96384/2); + + bcm_write32(BCMA_COMMAND, 0); + bcm_write32(0x10000C00, 0xC0000000); + + while (!(bcm_read32(0x10000C00) & 1)) + yield(); + + bcm_write32(0x10000C00, 0); + bcm_write32(0x10000400, 0xA5A50002); + + while (bcm_read32(BCMA_COMMAND) == 0) + yield(); + + /* sleep(HZ/2) apparently unneeded */ +} + +void lcd_enable(bool on) +{ + if (lcd_state.display_on != on && flash_vmcs_length != 0) + { + if(on) + { + /* Ensure BCM has been off for 1/2 s at least */ + while (!TIME_AFTER(current_tick, lcd_state.update_timeout)) + yield(); + + bcm_init(); + + /* Update LCD, but don't use lcd_update(). Instead, wait here + for command to complete so LCD isn't white when the backlight + turns on + */ + bcm_write_addr(BCMA_CMDPARAM); + lcd_write_data(&(lcd_framebuffer[0][0]), LCD_WIDTH * LCD_HEIGHT); + bcm_command(BCMCMD_LCD_UPDATE); + + lcd_state.state = LCD_INITIAL; + tick_add_task(&lcd_tick); + lcd_state.display_on = true; + lcd_call_enable_hook(); + } + else + { + lcd_state.display_on = false; + + /* Wait for BCM to finish work */ + while (lcd_state.state != LCD_INITIAL && lcd_state.state != LCD_IDLE) + yield(); + + tick_remove_task(&lcd_tick); + bcm_powerdown(); + bcm_off_wait = current_tick + HZ/2; + } + } +} + +bool lcd_enabled(void) +{ + return lcd_state.display_on; +} + +#ifdef HAVE_LCD_SLEEP +void lcd_sleep(void) { + lcd_enable(false); +} +#endif /* HAVE_LCD_SLEEP */ + +#ifdef HAVE_LCD_SHUTDOWN +void lcd_shutdown(void) { + lcd_enable(false); +} +#endif /* HAVE_LCD_SHUTDOWN */ +#endif /* HAVE_LCD_ENABLE */ Index: firmware/target/arm/ipod/power-ipod.c =================================================================== --- firmware/target/arm/ipod/power-ipod.c (revision 19970) +++ firmware/target/arm/ipod/power-ipod.c (working copy) @@ -125,7 +125,7 @@ void power_off(void) { -#if defined(HAVE_LCD_COLOR) +#if defined(HAVE_LCD_COLOR) && !defined(HAVE_LCD_SHUTDOWN) /* Clear the screen and backdrop to remove ghosting effect on shutdown */ lcd_set_backdrop(NULL);