Fixes and improvements for FS#9890 - iPod 5G: LCD sleep, BCM shutdown and bootstrap

* Ensure LCD is updated if lcd_update() is called while the LCD is initializing
* Turn on BCM and LCD in lcd_init_device() if it is off
* Speed up lcd_awake()
* Shorten minimum length of time BCM must stay off and fix related code

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@20695 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Boris Gjenero 2009-04-12 14:54:12 +00:00
parent 7ed1a5f120
commit 73e1bad355
2 changed files with 66 additions and 35 deletions

View file

@ -57,6 +57,8 @@
/* Time until the BCM is considered stalled and will be re-kicked.
* Must be guaranteed to be >~ 20ms. */
#define BCM_UPDATE_TIMEOUT (HZ/20)
/* An LCD update command done while the LCD is off needs >~ 200ms */
#define BCM_LCDINIT_TIMEOUT (HZ/2)
/* Addresses within BCM */
#define BCMA_SRAM_BASE 0
@ -92,7 +94,7 @@ enum lcd_status {
};
struct {
long update_timeout;
long update_timeout; /* also used to ensure BCM stays off for >= 50 ms */
enum lcd_status state;
bool blocked;
#if NUM_CORES > 1
@ -100,11 +102,12 @@ struct {
#endif
#ifdef HAVE_LCD_SLEEP
bool display_on;
bool waking;
struct wakeup initwakeup;
#endif
} lcd_state IBSS_ATTR;
#ifdef HAVE_LCD_SLEEP
static long bcm_off_wait;
const fb_data *flash_vmcs_offset;
unsigned flash_vmcs_length;
/* This mutex exists because enabling the backlight by changing a setting
@ -179,6 +182,12 @@ static inline unsigned bcm_read32(unsigned address)
return BCM_DATA32; /* read value */
}
static void continue_lcd_awake(void)
{
lcd_state.waking = false;
wakeup_signal(&(lcd_state.initwakeup));
}
#ifndef BOOTLOADER
static void lcd_tick(void)
{
@ -201,11 +210,15 @@ static void lcd_tick(void)
BCM_CONTROL = 0x31;
lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT;
lcd_state.state = LCD_UPDATING;
if (lcd_state.waking)
continue_lcd_awake();
}
else if ((lcd_state.state == LCD_UPDATING) && !bcm_is_busy)
{
/* Update finished properly and no new update pending. */
lcd_state.state = LCD_IDLE;
if (lcd_state.waking)
continue_lcd_awake();
}
}
#if NUM_CORES > 1
@ -246,6 +259,8 @@ static void lcd_unblock_and_update(void)
BCM_CONTROL = 0x31;
lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT;
lcd_state.state = LCD_UPDATING;
if (lcd_state.waking)
continue_lcd_awake();
}
else
{
@ -309,10 +324,7 @@ void lcd_init_device(void)
/* These port initializations are supposed to be done when initializing
the BCM. None of it is changed when shutting down the BCM.
*/
GPO32_ENABLE |= 0x4000;
/* GPO32_VAL & 0x8000 may supply power for BCM sleep state */
GPO32_ENABLE |= 0x8000;
GPO32_VAL &= ~0x8000;
GPO32_ENABLE |= 0xC000;
GPIO_CLEAR_BITWISE(GPIOC_ENABLE, 0x80);
/* This pin is used for BCM interrupts */
GPIOC_ENABLE |= 0x40;
@ -326,7 +338,6 @@ void lcd_init_device(void)
corelock_init(&lcd_state.cl);
#endif
#ifdef HAVE_LCD_SLEEP
lcd_state.display_on = true; /* Code in flash turned it on */
if (!flash_get_section(ROM_ID('v', 'm', 'c', 's'),
(void **)(&flash_vmcs_offset), &flash_vmcs_length))
/* BCM cannot be shut down because firmware wasn't found */
@ -336,8 +347,27 @@ void lcd_init_device(void)
flash_vmcs_length = ((flash_vmcs_length + 3) >> 1) & ~1;
}
mutex_init(&lcdstate_lock);
#endif
wakeup_init(&(lcd_state.initwakeup));
lcd_state.waking = false;
if (GPO32_VAL & 0x4000)
{
/* BCM is powered. Assume it is initialized. */
lcd_state.display_on = true;
tick_add_task(&lcd_tick);
}
else
{
/* BCM is not powered, so it needs to be initialized.
This can only happen when loading Rockbox via ROLO.
*/
lcd_state.update_timeout = current_tick;
lcd_state.display_on = false;
lcd_awake();
}
#else /* !HAVE_LCD_SLEEP */
tick_add_task(&lcd_tick);
#endif
#endif /* !BOOTLOADER */
}
@ -495,9 +525,7 @@ void bcm_init(void)
/* Power up BCM */
GPO32_VAL |= 0x4000;
/* Changed from HZ/2 to speed up this function */
sleep(HZ/8);
sleep(HZ/20);
/* Bootstrap stage 1 */
@ -511,7 +539,10 @@ void bcm_init(void)
*/
/* Bootstrap stage 2 */
while (BCM_ALT_CONTROL & 0x80);
while (!(BCM_ALT_CONTROL & 0x40));
for (i = 0; i < 8; i++) {
BCM_CONTROL = bcm_bootstrapdata[i];
}
@ -520,19 +551,15 @@ void bcm_init(void)
BCM_ALT_CONTROL = bcm_bootstrapdata[i];
}
while ((BCM_RD_ADDR & 1) == 0 || (BCM_ALT_RD_ADDR & 1) == 0)
yield();
while ((BCM_RD_ADDR & 1) == 0 || (BCM_ALT_RD_ADDR & 1) == 0);
(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();
while (BCM_ALT_CONTROL & 0x80);
while (!(BCM_ALT_CONTROL & 0x40));
/* Upload firmware to BCM SRAM */
bcm_write_addr(BCMA_SRAM_BASE);
@ -541,8 +568,7 @@ void bcm_init(void)
bcm_write32(BCMA_COMMAND, 0);
bcm_write32(0x10000C00, 0xC0000000);
while (!(bcm_read32(0x10000C00) & 1))
yield();
while (!(bcm_read32(0x10000C00) & 1));
bcm_write32(0x10000C00, 0);
bcm_write32(0x10000400, 0xA5A50002);
@ -558,23 +584,26 @@ void lcd_awake(void)
mutex_lock(&lcdstate_lock);
if (!lcd_state.display_on && flash_vmcs_length != 0)
{
/* Ensure BCM has been off for 1/2 s at least */
while (!TIME_AFTER(current_tick, lcd_state.update_timeout))
yield();
/* Ensure BCM has been off for >= 50 ms */
long sleepwait = lcd_state.update_timeout + HZ/20 - current_tick;
if (sleepwait > 0 && sleepwait < HZ/20)
sleep(sleepwait);
bcm_init();
/* Update LCD, but don't use lcd_update(). Instead, wait here
until the command completes 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);
/* Start the first LCD update, which also initializes the LCD */
lcd_state.state = LCD_INITIAL;
tick_add_task(&lcd_tick);
lcd_state.display_on = true;
lcd_update();
lcd_state.update_timeout = current_tick + BCM_LCDINIT_TIMEOUT;
/* Wait for end of first LCD update, so LCD isn't white
when the backlight turns on.
*/
lcd_state.waking = true;
tick_add_task(&lcd_tick);
wakeup_wait(&(lcd_state.initwakeup), TIMEOUT_BLOCK);
lcd_activation_call_hook();
}
mutex_unlock(&lcdstate_lock);
@ -592,7 +621,9 @@ void lcd_sleep(void)
tick_remove_task(&lcd_tick);
bcm_powerdown();
bcm_off_wait = current_tick + HZ/2;
/* Remember time to ensure BCM stays off for >= 50 ms */
lcd_state.update_timeout = current_tick;
}
mutex_unlock(&lcdstate_lock);
}