summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm')
-rw-r--r--firmware/target/arm/ipod/backlight-nano_video.c11
-rw-r--r--firmware/target/arm/ipod/backlight-target.h4
-rw-r--r--firmware/target/arm/ipod/power-ipod.c2
-rw-r--r--firmware/target/arm/ipod/video/lcd-video.c316
4 files changed, 306 insertions, 27 deletions
diff --git a/firmware/target/arm/ipod/backlight-nano_video.c b/firmware/target/arm/ipod/backlight-nano_video.c
index 5eb5198b73..2f56f94225 100644
--- a/firmware/target/arm/ipod/backlight-nano_video.c
+++ b/firmware/target/arm/ipod/backlight-nano_video.c
@@ -73,9 +73,18 @@ void _backlight_set_brightness(int val)
73 73
74void _backlight_hw_enable(bool on) 74void _backlight_hw_enable(bool on)
75{ 75{
76#ifdef HAVE_LCD_SLEEP
77 if (on)
78 /* If the fade-out is interrupted, enabled will be true, but
79 lcd_awake() needs to be called anyways because the LCD
80 may be sleeping.
81 */
82 lcd_awake();
83#endif
84
76 if (on == enabled) 85 if (on == enabled)
77 return; 86 return;
78 87
79 if (on) 88 if (on)
80 { 89 {
81 GPIO_SET_BITWISE(GPIOB_OUTPUT_VAL, 0x08); 90 GPIO_SET_BITWISE(GPIOB_OUTPUT_VAL, 0x08);
diff --git a/firmware/target/arm/ipod/backlight-target.h b/firmware/target/arm/ipod/backlight-target.h
index 50a7a0c7f1..28c519e4c0 100644
--- a/firmware/target/arm/ipod/backlight-target.h
+++ b/firmware/target/arm/ipod/backlight-target.h
@@ -29,6 +29,10 @@ void _backlight_led_on(void);
29void _backlight_led_off(void); 29void _backlight_led_off(void);
30void _backlight_hw_enable(bool on); 30void _backlight_hw_enable(bool on);
31 31
32#ifdef HAVE_LCD_SLEEP
33void lcd_awake(void);
34#endif
35
32#ifdef BOOTLOADER 36#ifdef BOOTLOADER
33#define _backlight_on() do { _backlight_hw_enable(true); \ 37#define _backlight_on() do { _backlight_hw_enable(true); \
34 _backlight_led_on(); } while(0) 38 _backlight_led_on(); } while(0)
diff --git a/firmware/target/arm/ipod/power-ipod.c b/firmware/target/arm/ipod/power-ipod.c
index 4c6df882c6..66d703859c 100644
--- a/firmware/target/arm/ipod/power-ipod.c
+++ b/firmware/target/arm/ipod/power-ipod.c
@@ -130,7 +130,7 @@ bool ide_powered(void)
130 130
131void power_off(void) 131void power_off(void)
132{ 132{
133#if defined(HAVE_LCD_COLOR) 133#if defined(HAVE_LCD_COLOR) && !defined(HAVE_LCD_SHUTDOWN)
134 /* Clear the screen and backdrop to 134 /* Clear the screen and backdrop to
135 remove ghosting effect on shutdown */ 135 remove ghosting effect on shutdown */
136 lcd_set_backdrop(NULL); 136 lcd_set_backdrop(NULL);
diff --git a/firmware/target/arm/ipod/video/lcd-video.c b/firmware/target/arm/ipod/video/lcd-video.c
index 6e5523d72c..d1701ea3d7 100644
--- a/firmware/target/arm/ipod/video/lcd-video.c
+++ b/firmware/target/arm/ipod/video/lcd-video.c
@@ -30,6 +30,10 @@
30#include "lcd.h" 30#include "lcd.h"
31#include "kernel.h" 31#include "kernel.h"
32#include "system.h" 32#include "system.h"
33#ifdef HAVE_LCD_SLEEP
34/* Included only for lcd_awake() prototype */
35#include "backlight-target.h"
36#endif
33 37
34/* The BCM bus width is 16 bits. But since the low address bits aren't decoded 38/* The BCM bus width is 16 bits. But since the low address bits aren't decoded
35 * by the chip (the 3 BCM address bits are mapped to address bits 16..18 of the 39 * by the chip (the 3 BCM address bits are mapped to address bits 16..18 of the
@@ -50,12 +54,36 @@
50#define BCM_ALT_RD_ADDR32 (*(volatile unsigned long *)(0x30060000)) 54#define BCM_ALT_RD_ADDR32 (*(volatile unsigned long *)(0x30060000))
51#define BCM_ALT_CONTROL (*(volatile unsigned short*)(0x30070000)) 55#define BCM_ALT_CONTROL (*(volatile unsigned short*)(0x30070000))
52 56
53#define BCM_FB_BASE 0xE0020 /* Address of internal BCM framebuffer */
54
55/* 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.
56 * Must be guaranteed to be >~ 20ms. */ 58 * Must be guaranteed to be >~ 20ms. */
57#define BCM_UPDATE_TIMEOUT (HZ/20) 59#define BCM_UPDATE_TIMEOUT (HZ/20)
58 60
61/* Addresses within BCM */
62#define BCMA_SRAM_BASE 0
63#define BCMA_COMMAND 0x1F8
64#define BCMA_STATUS 0x1FC
65#define BCMA_CMDPARAM 0xE0000 /* Parameters/data for commands */
66#define BCMA_SDRAM_BASE 0xC0000000
67#define BCMA_TV_FB 0xC0000000 /* TV out framebuffer */
68#define BCMA_TV_BMPDATA 0xC0200000 /* BMP data for TV out functions */
69
70/* BCM commands. Write them to BCMA_COMMAND. Note BCM_CMD encoding. */
71#define BCM_CMD(x) ((~((unsigned long)x) << 16) | ((unsigned long)x))
72#define BCMCMD_LCD_UPDATE BCM_CMD(0)
73/* Execute "M25 Diagnostics". Status displayed on LCD. Takes <40s */
74#define BCMCMD_SELFTEST BCM_CMD(1)
75#define BCMCMD_TV_PALBMP BCM_CMD(2)
76#define BCMCMD_TV_NTSCBMP BCM_CMD(3)
77/* BCM_CMD(4) may be another TV-related command */
78/* The following might do more depending on word at 0xE00000 */
79#define BCMCMD_LCD_UPDATERECT BCM_CMD(5)
80#define BCMCMD_LCD_SLEEP BCM_CMD(8)
81/* BCM_CMD(12) involved in shutdown */
82/* Macrovision analog copy prevention is on by default on TV output.
83 Execute this command after enabling TV out to turn it off.
84 */
85#define BCMCMD_TV_MVOFF BCM_CMD(14)
86
59enum lcd_status { 87enum lcd_status {
60 LCD_IDLE, 88 LCD_IDLE,
61 LCD_INITIAL, 89 LCD_INITIAL,
@@ -70,8 +98,60 @@ struct {
70#if NUM_CORES > 1 98#if NUM_CORES > 1
71 struct corelock cl; /* inter-core sync */ 99 struct corelock cl; /* inter-core sync */
72#endif 100#endif
101#ifdef HAVE_LCD_SLEEP
102 bool display_on;
103#endif
73} lcd_state IBSS_ATTR; 104} lcd_state IBSS_ATTR;
74 105
106#ifdef HAVE_LCD_SLEEP
107static long bcm_off_wait;
108const fb_data *flash_vmcs_offset;
109unsigned flash_vmcs_length;
110/* This mutex exists because enabling the backlight by changing a setting
111 will cause multiple concurrent lcd_wake() calls.
112 */
113static struct mutex lcdstate_lock SHAREDBSS_ATTR;
114
115#define ROM_BASE 0x20000000
116#define ROM_ID(a,b,c,d) (unsigned int)( ((unsigned int)(d)) | \
117 (((unsigned int)(c)) << 8) | \
118 (((unsigned int)(b)) << 16) | \
119 (((unsigned int)(a)) << 24) )
120
121/* Get address and length of iPod flash section.
122 Based on part of FS#6721. This may belong elsewhere.
123 (BCM initialization uploads the vmcs section to the BCM.)
124 */
125bool flash_get_section(const unsigned int imageid,
126 void **offset,
127 unsigned int *length)
128{
129 unsigned long *p = (unsigned long*)(ROM_BASE + 0xffe00);
130 unsigned char *csp, *csend;
131 unsigned long checksum;
132
133 /* Find the image in the directory */
134 while (1) {
135 if (p[0] != ROM_ID('f','l','s','h'))
136 return false;
137 if (p[1] == imageid)
138 break;
139 p += 10;
140 }
141
142 *offset = (void *)(ROM_BASE + p[3]);
143 *length = p[4];
144
145 /* Verify checksum. Probably unnecessary, but it's fast. */
146 checksum = 0;
147 csend = (unsigned char *)(ROM_BASE + p[3] + p[4]);
148 for(csp = (unsigned char *)(ROM_BASE + p[3]); csp < csend; csp++) {
149 checksum += *csp;
150 }
151
152 return checksum == p[7];
153}
154#endif /* HAVE_LCD_SLEEP */
75 155
76static inline void bcm_write_addr(unsigned address) 156static inline void bcm_write_addr(unsigned address)
77{ 157{
@@ -99,16 +179,6 @@ static inline unsigned bcm_read32(unsigned address)
99 return BCM_DATA32; /* read value */ 179 return BCM_DATA32; /* read value */
100} 180}
101 181
102static void bcm_setup_rect(unsigned x, unsigned y,
103 unsigned width, unsigned height)
104{
105 bcm_write_addr(0xE0004);
106 BCM_DATA32 = x;
107 BCM_DATA32 = y;
108 BCM_DATA32 = x + width - 1;
109 BCM_DATA32 = y + height - 1;
110}
111
112#ifndef BOOTLOADER 182#ifndef BOOTLOADER
113static void lcd_tick(void) 183static void lcd_tick(void)
114{ 184{
@@ -119,15 +189,15 @@ static void lcd_tick(void)
119 189
120 if (!lcd_state.blocked && lcd_state.state >= LCD_NEED_UPDATE) 190 if (!lcd_state.blocked && lcd_state.state >= LCD_NEED_UPDATE)
121 { 191 {
122 unsigned data = bcm_read32(0x1F8); 192 unsigned data = bcm_read32(BCMA_COMMAND);
123 bool bcm_is_busy = (data == 0xFFFA0005 || data == 0xFFFF); 193 bool bcm_is_busy = (data == BCMCMD_LCD_UPDATE || data == 0xFFFF);
124 194
125 if (((lcd_state.state == LCD_NEED_UPDATE) && !bcm_is_busy) 195 if (((lcd_state.state == LCD_NEED_UPDATE) && !bcm_is_busy)
126 /* Update requested and BCM is no longer busy. */ 196 /* Update requested and BCM is no longer busy. */
127 || (TIME_AFTER(current_tick, lcd_state.update_timeout) && bcm_is_busy)) 197 || (TIME_AFTER(current_tick, lcd_state.update_timeout) && bcm_is_busy))
128 /* BCM still busy after timeout, i.e. stalled. */ 198 /* BCM still busy after timeout, i.e. stalled. */
129 { 199 {
130 bcm_write32(0x1F8, 0xFFFA0005); /* Kick off update */ 200 bcm_write32(BCMA_COMMAND, BCMCMD_LCD_UPDATE); /* Kick off update */
131 BCM_CONTROL = 0x31; 201 BCM_CONTROL = 0x31;
132 lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT; 202 lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT;
133 lcd_state.state = LCD_UPDATING; 203 lcd_state.state = LCD_UPDATING;
@@ -166,13 +236,13 @@ static void lcd_unblock_and_update(void)
166#if NUM_CORES > 1 236#if NUM_CORES > 1
167 corelock_lock(&lcd_state.cl); 237 corelock_lock(&lcd_state.cl);
168#endif 238#endif
169 data = bcm_read32(0x1F8); 239 data = bcm_read32(BCMA_COMMAND);
170 bcm_is_busy = (data == 0xFFFA0005 || data == 0xFFFF); 240 bcm_is_busy = (data == BCMCMD_LCD_UPDATE || data == 0xFFFF);
171 241
172 if (!bcm_is_busy || (lcd_state.state == LCD_INITIAL) || 242 if (!bcm_is_busy || (lcd_state.state == LCD_INITIAL) ||
173 TIME_AFTER(current_tick, lcd_state.update_timeout)) 243 TIME_AFTER(current_tick, lcd_state.update_timeout))
174 { 244 {
175 bcm_write32(0x1F8, 0xFFFA0005); /* Kick off update */ 245 bcm_write32(BCMA_COMMAND, BCMCMD_LCD_UPDATE); /* Kick off update */
176 BCM_CONTROL = 0x31; 246 BCM_CONTROL = 0x31;
177 lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT; 247 lcd_state.update_timeout = current_tick + BCM_UPDATE_TIMEOUT;
178 lcd_state.state = LCD_UPDATING; 248 lcd_state.state = LCD_UPDATING;
@@ -199,14 +269,14 @@ static void lcd_unblock_and_update(void)
199 269
200 if (lcd_state.state != LCD_INITIAL) 270 if (lcd_state.state != LCD_INITIAL)
201 { 271 {
202 data = bcm_read32(0x1F8); 272 data = bcm_read32(BCMA_COMMAND);
203 while (data == 0xFFFA0005 || data == 0xFFFF) 273 while (data == BCMCMD_LCD_UPDATE || data == 0xFFFF)
204 { 274 {
205 yield(); 275 yield();
206 data = bcm_read32(0x1F8); 276 data = bcm_read32(BCMA_COMMAND);
207 } 277 }
208 } 278 }
209 bcm_write32(0x1F8, 0xFFFA0005); /* Kick off update */ 279 bcm_write32(BCMA_COMMAND, BCMCMD_LCD_UPDATE); /* Kick off update */
210 BCM_CONTROL = 0x31; 280 BCM_CONTROL = 0x31;
211 lcd_state.state = LCD_IDLE; 281 lcd_state.state = LCD_IDLE;
212} 282}
@@ -236,13 +306,37 @@ void lcd_set_flip(bool yesno)
236/* LCD init */ 306/* LCD init */
237void lcd_init_device(void) 307void lcd_init_device(void)
238{ 308{
239 bcm_setup_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); 309 /* These port initializations are supposed to be done when initializing
310 the BCM. None of it is changed when shutting down the BCM.
311 */
312 GPO32_ENABLE |= 0x4000;
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);
317 /* This pin is used for BCM interrupts */
318 GPIOC_ENABLE |= 0x40;
319 GPIOC_OUTPUT_EN &= ~0x40;
320 GPO32_ENABLE &= ~1;
321
240 lcd_state.blocked = false; 322 lcd_state.blocked = false;
241 lcd_state.state = LCD_INITIAL; 323 lcd_state.state = LCD_INITIAL;
242#ifndef BOOTLOADER 324#ifndef BOOTLOADER
243#if NUM_CORES > 1 325#if NUM_CORES > 1
244 corelock_init(&lcd_state.cl); 326 corelock_init(&lcd_state.cl);
245#endif 327#endif
328#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'),
331 (void **)(&flash_vmcs_offset), &flash_vmcs_length))
332 /* BCM cannot be shut down because firmware wasn't found */
333 flash_vmcs_length = 0;
334 else {
335 /* lcd_write_data needs an even number of 16 bit values */
336 flash_vmcs_length = ((flash_vmcs_length + 3) >> 1) & ~1;
337 }
338 mutex_init(&lcdstate_lock);
339#endif
246 tick_add_task(&lcd_tick); 340 tick_add_task(&lcd_tick);
247#endif /* !BOOTLOADER */ 341#endif /* !BOOTLOADER */
248} 342}
@@ -255,6 +349,11 @@ void lcd_update_rect(int x, int y, int width, int height)
255 const fb_data *addr; 349 const fb_data *addr;
256 unsigned bcmaddr; 350 unsigned bcmaddr;
257 351
352#ifdef HAVE_LCD_SLEEP
353 if (!lcd_state.display_on)
354 return;
355#endif
356
258 if (x + width >= LCD_WIDTH) 357 if (x + width >= LCD_WIDTH)
259 width = LCD_WIDTH - x; 358 width = LCD_WIDTH - x;
260 if (y + height >= LCD_HEIGHT) 359 if (y + height >= LCD_HEIGHT)
@@ -272,7 +371,7 @@ void lcd_update_rect(int x, int y, int width, int height)
272 lcd_block_tick(); 371 lcd_block_tick();
273 372
274 addr = &lcd_framebuffer[y][x]; 373 addr = &lcd_framebuffer[y][x];
275 bcmaddr = BCM_FB_BASE + (LCD_WIDTH*2) * y + (x << 1); 374 bcmaddr = BCMA_CMDPARAM + (LCD_WIDTH*2) * y + (x << 1);
276 375
277 if (width == LCD_WIDTH) 376 if (width == LCD_WIDTH)
278 { 377 {
@@ -315,6 +414,11 @@ void lcd_blit_yuv(unsigned char * const src[3],
315 off_t z; 414 off_t z;
316 unsigned char const * yuv_src[3]; 415 unsigned char const * yuv_src[3];
317 416
417#ifdef HAVE_LCD_SLEEP
418 if (!lcd_state.display_on)
419 return;
420#endif
421
318 /* Sorry, but width and height must be >= 2 or else */ 422 /* Sorry, but width and height must be >= 2 or else */
319 width &= ~1; 423 width &= ~1;
320 424
@@ -326,7 +430,7 @@ void lcd_blit_yuv(unsigned char * const src[3],
326 /* Prevent the tick from triggering BCM updates while we're writing. */ 430 /* Prevent the tick from triggering BCM updates while we're writing. */
327 lcd_block_tick(); 431 lcd_block_tick();
328 432
329 bcmaddr = BCM_FB_BASE + (LCD_WIDTH*2) * y + (x << 1); 433 bcmaddr = BCMA_CMDPARAM + (LCD_WIDTH*2) * y + (x << 1);
330 height >>= 1; 434 height >>= 1;
331 435
332 do 436 do
@@ -341,3 +445,165 @@ void lcd_blit_yuv(unsigned char * const src[3],
341 445
342 lcd_unblock_and_update(); 446 lcd_unblock_and_update();
343} 447}
448
449#ifdef HAVE_LCD_SLEEP
450/* Executes a BCM command immediately and waits for it to complete.
451 Other BCM commands (eg. LCD updates or lcd_tick) must not interfere.
452 */
453static void bcm_command(unsigned cmd) {
454 unsigned status;
455
456 bcm_write32(BCMA_COMMAND, cmd);
457
458 BCM_CONTROL = 0x31;
459
460 while (1) {
461 status = bcm_read32(BCMA_COMMAND);
462 if (status != cmd && status != 0xFFFF)
463 break;
464 yield();
465 }
466}
467
468void bcm_powerdown(void)
469{
470 bcm_write32(0x10001400, bcm_read32(0x10001400) & ~0xF0);
471
472 /* Blanks the LCD and decreases power consumption
473 below what clearing the LCD would achieve.
474 Executing an LCD update command wakes it.
475 */
476 bcm_command(BCMCMD_LCD_SLEEP);
477
478 /* Not sure if this does anything */
479 bcm_command(BCM_CMD(0xC));
480
481 /* Further cuts power use, probably by powering down BCM.
482 After this point, BCM needs to be bootstrapped
483 */
484 GPO32_VAL &= ~0x4000;
485}
486
487/* Data written to BCM_CONTROL and BCM_ALT_CONTROL */
488const unsigned char bcm_bootstrapdata[] = {
489 0xA1, 0x81, 0x91, 0x02, 0x12, 0x22, 0x72, 0x62
490};
491
492void bcm_init(void)
493{
494 int i;
495
496 /* Power up BCM */
497 GPO32_VAL |= 0x4000;
498
499 /* Changed from HZ/2 to speed up this function */
500 sleep(HZ/8);
501
502 /* Bootstrap stage 1 */
503
504 STRAP_OPT_A &= ~0xF00;
505 outl(0x1313, 0x70000040);
506
507 /* Interrupt-related code for future use
508 GPIOC_INT_LEV |= 0x40;
509 GPIOC_INT_EN |= 0x40;
510 CPU_HI_INT_EN |= 0x40000;
511 */
512
513 /* Bootstrap stage 2 */
514
515 for (i = 0; i < 8; i++) {
516 BCM_CONTROL = bcm_bootstrapdata[i];
517 }
518
519 for (i = 3; i < 8; i++) {
520 BCM_ALT_CONTROL = bcm_bootstrapdata[i];
521 }
522
523 while ((BCM_RD_ADDR & 1) == 0 || (BCM_ALT_RD_ADDR & 1) == 0)
524 yield();
525
526 (void)BCM_WR_ADDR;
527 (void)BCM_ALT_WR_ADDR;
528
529 /* Bootstrap stage 3: upload firmware */
530
531 while (BCM_ALT_CONTROL & 0x80)
532 yield();
533
534 while (!(BCM_ALT_CONTROL & 0x40))
535 yield();
536
537 /* Upload firmware to BCM SRAM */
538 bcm_write_addr(BCMA_SRAM_BASE);
539 lcd_write_data(flash_vmcs_offset, flash_vmcs_length);
540
541 bcm_write32(BCMA_COMMAND, 0);
542 bcm_write32(0x10000C00, 0xC0000000);
543
544 while (!(bcm_read32(0x10000C00) & 1))
545 yield();
546
547 bcm_write32(0x10000C00, 0);
548 bcm_write32(0x10000400, 0xA5A50002);
549
550 while (bcm_read32(BCMA_COMMAND) == 0)
551 yield();
552
553 /* sleep(HZ/2) apparently unneeded */
554}
555
556void lcd_awake(void)
557{
558 mutex_lock(&lcdstate_lock);
559 if (!lcd_state.display_on && flash_vmcs_length != 0)
560 {
561 /* Ensure BCM has been off for 1/2 s at least */
562 while (!TIME_AFTER(current_tick, lcd_state.update_timeout))
563 yield();
564
565 bcm_init();
566
567 /* Update LCD, but don't use lcd_update(). Instead, wait here
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;
576 tick_add_task(&lcd_tick);
577 lcd_state.display_on = true;
578 /* Note that only the RGB data from lcd_framebuffer has been
579 displayed. If YUV data was displayed, it needs to be updated
580 now. (eg. see lcd_call_enable_hook())
581 */
582 }
583 mutex_unlock(&lcdstate_lock);
584}
585
586void lcd_sleep(void)
587{
588 mutex_lock(&lcdstate_lock);
589 if (lcd_state.display_on && flash_vmcs_length != 0) {
590 lcd_state.display_on = false;
591
592 /* Wait for BCM to finish work */
593 while (lcd_state.state != LCD_INITIAL && lcd_state.state != LCD_IDLE)
594 yield();
595
596 tick_remove_task(&lcd_tick);
597 bcm_powerdown();
598 bcm_off_wait = current_tick + HZ/2;
599 }
600 mutex_unlock(&lcdstate_lock);
601}
602
603#ifdef HAVE_LCD_SHUTDOWN
604void lcd_shutdown(void)
605{
606 lcd_sleep();
607}
608#endif /* HAVE_LCD_SHUTDOWN */
609#endif /* HAVE_LCD_SLEEP */