diff options
Diffstat (limited to 'firmware/target/arm/ipod/video/lcd-video.c')
-rw-r--r-- | firmware/target/arm/ipod/video/lcd-video.c | 99 |
1 files changed, 65 insertions, 34 deletions
diff --git a/firmware/target/arm/ipod/video/lcd-video.c b/firmware/target/arm/ipod/video/lcd-video.c index 0a2c5985a9..200126d837 100644 --- a/firmware/target/arm/ipod/video/lcd-video.c +++ b/firmware/target/arm/ipod/video/lcd-video.c | |||
@@ -57,6 +57,8 @@ | |||
57 | /* Time until the BCM is considered stalled and will be re-kicked. | 57 | /* Time until the BCM is considered stalled and will be re-kicked. |
58 | * Must be guaranteed to be >~ 20ms. */ | 58 | * Must be guaranteed to be >~ 20ms. */ |
59 | #define BCM_UPDATE_TIMEOUT (HZ/20) | 59 | #define BCM_UPDATE_TIMEOUT (HZ/20) |
60 | /* An LCD update command done while the LCD is off needs >~ 200ms */ | ||
61 | #define BCM_LCDINIT_TIMEOUT (HZ/2) | ||
60 | 62 | ||
61 | /* Addresses within BCM */ | 63 | /* Addresses within BCM */ |
62 | #define BCMA_SRAM_BASE 0 | 64 | #define BCMA_SRAM_BASE 0 |
@@ -92,7 +94,7 @@ enum lcd_status { | |||
92 | }; | 94 | }; |
93 | 95 | ||
94 | struct { | 96 | struct { |
95 | long update_timeout; | 97 | long update_timeout; /* also used to ensure BCM stays off for >= 50 ms */ |
96 | enum lcd_status state; | 98 | enum lcd_status state; |
97 | bool blocked; | 99 | bool blocked; |
98 | #if NUM_CORES > 1 | 100 | #if NUM_CORES > 1 |
@@ -100,11 +102,12 @@ struct { | |||
100 | #endif | 102 | #endif |
101 | #ifdef HAVE_LCD_SLEEP | 103 | #ifdef HAVE_LCD_SLEEP |
102 | bool display_on; | 104 | bool display_on; |
105 | bool waking; | ||
106 | struct wakeup initwakeup; | ||
103 | #endif | 107 | #endif |
104 | } lcd_state IBSS_ATTR; | 108 | } lcd_state IBSS_ATTR; |
105 | 109 | ||
106 | #ifdef HAVE_LCD_SLEEP | 110 | #ifdef HAVE_LCD_SLEEP |
107 | static long bcm_off_wait; | ||
108 | const fb_data *flash_vmcs_offset; | 111 | const fb_data *flash_vmcs_offset; |
109 | unsigned flash_vmcs_length; | 112 | unsigned flash_vmcs_length; |
110 | /* This mutex exists because enabling the backlight by changing a setting | 113 | /* This mutex exists because enabling the backlight by changing a setting |
@@ -179,6 +182,12 @@ static inline unsigned bcm_read32(unsigned address) | |||
179 | return BCM_DATA32; /* read value */ | 182 | return BCM_DATA32; /* read value */ |
180 | } | 183 | } |
181 | 184 | ||
185 | static void continue_lcd_awake(void) | ||
186 | { | ||
187 | lcd_state.waking = false; | ||
188 | wakeup_signal(&(lcd_state.initwakeup)); | ||
189 | } | ||
190 | |||
182 | #ifndef BOOTLOADER | 191 | #ifndef BOOTLOADER |
183 | static void lcd_tick(void) | 192 | static void lcd_tick(void) |
184 | { | 193 | { |
@@ -201,11 +210,15 @@ static void lcd_tick(void) | |||
201 | BCM_CONTROL = 0x31; | 210 | BCM_CONTROL = 0x31; |
202 | lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT; | 211 | lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT; |
203 | lcd_state.state = LCD_UPDATING; | 212 | lcd_state.state = LCD_UPDATING; |
213 | if (lcd_state.waking) | ||
214 | continue_lcd_awake(); | ||
204 | } | 215 | } |
205 | else if ((lcd_state.state == LCD_UPDATING) && !bcm_is_busy) | 216 | else if ((lcd_state.state == LCD_UPDATING) && !bcm_is_busy) |
206 | { | 217 | { |
207 | /* Update finished properly and no new update pending. */ | 218 | /* Update finished properly and no new update pending. */ |
208 | lcd_state.state = LCD_IDLE; | 219 | lcd_state.state = LCD_IDLE; |
220 | if (lcd_state.waking) | ||
221 | continue_lcd_awake(); | ||
209 | } | 222 | } |
210 | } | 223 | } |
211 | #if NUM_CORES > 1 | 224 | #if NUM_CORES > 1 |
@@ -246,6 +259,8 @@ static void lcd_unblock_and_update(void) | |||
246 | BCM_CONTROL = 0x31; | 259 | BCM_CONTROL = 0x31; |
247 | lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT; | 260 | lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT; |
248 | lcd_state.state = LCD_UPDATING; | 261 | lcd_state.state = LCD_UPDATING; |
262 | if (lcd_state.waking) | ||
263 | continue_lcd_awake(); | ||
249 | } | 264 | } |
250 | else | 265 | else |
251 | { | 266 | { |
@@ -309,10 +324,7 @@ void lcd_init_device(void) | |||
309 | /* These port initializations are supposed to be done when initializing | 324 | /* These port initializations are supposed to be done when initializing |
310 | the BCM. None of it is changed when shutting down the BCM. | 325 | the BCM. None of it is changed when shutting down the BCM. |
311 | */ | 326 | */ |
312 | GPO32_ENABLE |= 0x4000; | 327 | GPO32_ENABLE |= 0xC000; |
313 | /* GPO32_VAL & 0x8000 may supply power for BCM sleep state */ | ||
314 | GPO32_ENABLE |= 0x8000; | ||
315 | GPO32_VAL &= ~0x8000; | ||
316 | GPIO_CLEAR_BITWISE(GPIOC_ENABLE, 0x80); | 328 | GPIO_CLEAR_BITWISE(GPIOC_ENABLE, 0x80); |
317 | /* This pin is used for BCM interrupts */ | 329 | /* This pin is used for BCM interrupts */ |
318 | GPIOC_ENABLE |= 0x40; | 330 | GPIOC_ENABLE |= 0x40; |
@@ -326,7 +338,6 @@ void lcd_init_device(void) | |||
326 | corelock_init(&lcd_state.cl); | 338 | corelock_init(&lcd_state.cl); |
327 | #endif | 339 | #endif |
328 | #ifdef HAVE_LCD_SLEEP | 340 | #ifdef HAVE_LCD_SLEEP |
329 | lcd_state.display_on = true; /* Code in flash turned it on */ | ||
330 | if (!flash_get_section(ROM_ID('v', 'm', 'c', 's'), | 341 | if (!flash_get_section(ROM_ID('v', 'm', 'c', 's'), |
331 | (void **)(&flash_vmcs_offset), &flash_vmcs_length)) | 342 | (void **)(&flash_vmcs_offset), &flash_vmcs_length)) |
332 | /* BCM cannot be shut down because firmware wasn't found */ | 343 | /* BCM cannot be shut down because firmware wasn't found */ |
@@ -336,8 +347,27 @@ void lcd_init_device(void) | |||
336 | flash_vmcs_length = ((flash_vmcs_length + 3) >> 1) & ~1; | 347 | flash_vmcs_length = ((flash_vmcs_length + 3) >> 1) & ~1; |
337 | } | 348 | } |
338 | mutex_init(&lcdstate_lock); | 349 | mutex_init(&lcdstate_lock); |
339 | #endif | 350 | wakeup_init(&(lcd_state.initwakeup)); |
351 | lcd_state.waking = false; | ||
352 | |||
353 | if (GPO32_VAL & 0x4000) | ||
354 | { | ||
355 | /* BCM is powered. Assume it is initialized. */ | ||
356 | lcd_state.display_on = true; | ||
357 | tick_add_task(&lcd_tick); | ||
358 | } | ||
359 | else | ||
360 | { | ||
361 | /* BCM is not powered, so it needs to be initialized. | ||
362 | This can only happen when loading Rockbox via ROLO. | ||
363 | */ | ||
364 | lcd_state.update_timeout = current_tick; | ||
365 | lcd_state.display_on = false; | ||
366 | lcd_awake(); | ||
367 | } | ||
368 | #else /* !HAVE_LCD_SLEEP */ | ||
340 | tick_add_task(&lcd_tick); | 369 | tick_add_task(&lcd_tick); |
370 | #endif | ||
341 | #endif /* !BOOTLOADER */ | 371 | #endif /* !BOOTLOADER */ |
342 | } | 372 | } |
343 | 373 | ||
@@ -495,9 +525,7 @@ void bcm_init(void) | |||
495 | 525 | ||
496 | /* Power up BCM */ | 526 | /* Power up BCM */ |
497 | GPO32_VAL |= 0x4000; | 527 | GPO32_VAL |= 0x4000; |
498 | 528 | sleep(HZ/20); | |
499 | /* Changed from HZ/2 to speed up this function */ | ||
500 | sleep(HZ/8); | ||
501 | 529 | ||
502 | /* Bootstrap stage 1 */ | 530 | /* Bootstrap stage 1 */ |
503 | 531 | ||
@@ -511,7 +539,10 @@ void bcm_init(void) | |||
511 | */ | 539 | */ |
512 | 540 | ||
513 | /* Bootstrap stage 2 */ | 541 | /* Bootstrap stage 2 */ |
514 | 542 | ||
543 | while (BCM_ALT_CONTROL & 0x80); | ||
544 | while (!(BCM_ALT_CONTROL & 0x40)); | ||
545 | |||
515 | for (i = 0; i < 8; i++) { | 546 | for (i = 0; i < 8; i++) { |
516 | BCM_CONTROL = bcm_bootstrapdata[i]; | 547 | BCM_CONTROL = bcm_bootstrapdata[i]; |
517 | } | 548 | } |
@@ -520,19 +551,15 @@ void bcm_init(void) | |||
520 | BCM_ALT_CONTROL = bcm_bootstrapdata[i]; | 551 | BCM_ALT_CONTROL = bcm_bootstrapdata[i]; |
521 | } | 552 | } |
522 | 553 | ||
523 | while ((BCM_RD_ADDR & 1) == 0 || (BCM_ALT_RD_ADDR & 1) == 0) | 554 | while ((BCM_RD_ADDR & 1) == 0 || (BCM_ALT_RD_ADDR & 1) == 0); |
524 | yield(); | ||
525 | 555 | ||
526 | (void)BCM_WR_ADDR; | 556 | (void)BCM_WR_ADDR; |
527 | (void)BCM_ALT_WR_ADDR; | 557 | (void)BCM_ALT_WR_ADDR; |
528 | 558 | ||
529 | /* Bootstrap stage 3: upload firmware */ | 559 | /* Bootstrap stage 3: upload firmware */ |
530 | 560 | ||
531 | while (BCM_ALT_CONTROL & 0x80) | 561 | while (BCM_ALT_CONTROL & 0x80); |
532 | yield(); | 562 | while (!(BCM_ALT_CONTROL & 0x40)); |
533 | |||
534 | while (!(BCM_ALT_CONTROL & 0x40)) | ||
535 | yield(); | ||
536 | 563 | ||
537 | /* Upload firmware to BCM SRAM */ | 564 | /* Upload firmware to BCM SRAM */ |
538 | bcm_write_addr(BCMA_SRAM_BASE); | 565 | bcm_write_addr(BCMA_SRAM_BASE); |
@@ -541,8 +568,7 @@ void bcm_init(void) | |||
541 | bcm_write32(BCMA_COMMAND, 0); | 568 | bcm_write32(BCMA_COMMAND, 0); |
542 | bcm_write32(0x10000C00, 0xC0000000); | 569 | bcm_write32(0x10000C00, 0xC0000000); |
543 | 570 | ||
544 | while (!(bcm_read32(0x10000C00) & 1)) | 571 | while (!(bcm_read32(0x10000C00) & 1)); |
545 | yield(); | ||
546 | 572 | ||
547 | bcm_write32(0x10000C00, 0); | 573 | bcm_write32(0x10000C00, 0); |
548 | bcm_write32(0x10000400, 0xA5A50002); | 574 | bcm_write32(0x10000400, 0xA5A50002); |
@@ -558,23 +584,26 @@ void lcd_awake(void) | |||
558 | mutex_lock(&lcdstate_lock); | 584 | mutex_lock(&lcdstate_lock); |
559 | if (!lcd_state.display_on && flash_vmcs_length != 0) | 585 | if (!lcd_state.display_on && flash_vmcs_length != 0) |
560 | { | 586 | { |
561 | /* Ensure BCM has been off for 1/2 s at least */ | 587 | /* Ensure BCM has been off for >= 50 ms */ |
562 | while (!TIME_AFTER(current_tick, lcd_state.update_timeout)) | 588 | long sleepwait = lcd_state.update_timeout + HZ/20 - current_tick; |
563 | yield(); | 589 | if (sleepwait > 0 && sleepwait < HZ/20) |
590 | sleep(sleepwait); | ||
564 | 591 | ||
565 | bcm_init(); | 592 | bcm_init(); |
566 | 593 | ||
567 | /* Update LCD, but don't use lcd_update(). Instead, wait here | 594 | /* Start the first LCD update, which also initializes the LCD */ |
568 | until the command completes so LCD isn't white when the | ||
569 | backlight turns on | ||
570 | */ | ||
571 | bcm_write_addr(BCMA_CMDPARAM); | ||
572 | lcd_write_data(&(lcd_framebuffer[0][0]), LCD_WIDTH * LCD_HEIGHT); | ||
573 | bcm_command(BCMCMD_LCD_UPDATE); | ||
574 | |||
575 | lcd_state.state = LCD_INITIAL; | 595 | lcd_state.state = LCD_INITIAL; |
576 | tick_add_task(&lcd_tick); | ||
577 | lcd_state.display_on = true; | 596 | lcd_state.display_on = true; |
597 | lcd_update(); | ||
598 | lcd_state.update_timeout = current_tick + BCM_LCDINIT_TIMEOUT; | ||
599 | |||
600 | /* Wait for end of first LCD update, so LCD isn't white | ||
601 | when the backlight turns on. | ||
602 | */ | ||
603 | lcd_state.waking = true; | ||
604 | tick_add_task(&lcd_tick); | ||
605 | wakeup_wait(&(lcd_state.initwakeup), TIMEOUT_BLOCK); | ||
606 | |||
578 | lcd_activation_call_hook(); | 607 | lcd_activation_call_hook(); |
579 | } | 608 | } |
580 | mutex_unlock(&lcdstate_lock); | 609 | mutex_unlock(&lcdstate_lock); |
@@ -592,7 +621,9 @@ void lcd_sleep(void) | |||
592 | 621 | ||
593 | tick_remove_task(&lcd_tick); | 622 | tick_remove_task(&lcd_tick); |
594 | bcm_powerdown(); | 623 | bcm_powerdown(); |
595 | bcm_off_wait = current_tick + HZ/2; | 624 | |
625 | /* Remember time to ensure BCM stays off for >= 50 ms */ | ||
626 | lcd_state.update_timeout = current_tick; | ||
596 | } | 627 | } |
597 | mutex_unlock(&lcdstate_lock); | 628 | mutex_unlock(&lcdstate_lock); |
598 | } | 629 | } |