diff options
-rw-r--r-- | firmware/export/config-ipodvideo.h | 8 | ||||
-rw-r--r-- | firmware/target/arm/ipod/backlight-nano_video.c | 11 | ||||
-rw-r--r-- | firmware/target/arm/ipod/backlight-target.h | 4 | ||||
-rw-r--r-- | firmware/target/arm/ipod/power-ipod.c | 2 | ||||
-rw-r--r-- | firmware/target/arm/ipod/video/lcd-video.c | 316 |
5 files changed, 314 insertions, 27 deletions
diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h index ca8e43db77..fb00fd99d1 100644 --- a/firmware/export/config-ipodvideo.h +++ b/firmware/export/config-ipodvideo.h | |||
@@ -204,4 +204,12 @@ | |||
204 | #define IPOD_ACCESSORY_PROTOCOL | 204 | #define IPOD_ACCESSORY_PROTOCOL |
205 | #define HAVE_SERIAL | 205 | #define HAVE_SERIAL |
206 | 206 | ||
207 | #ifndef BOOTLOADER | ||
208 | /* Support for LCD sleep/BCM shutdown */ | ||
209 | #define HAVE_LCD_SLEEP | ||
210 | #define HAVE_LCD_SLEEP_SETTING | ||
211 | /* The same code may also be used when shutting down the iPod */ | ||
212 | #define HAVE_LCD_SHUTDOWN | ||
213 | #endif | ||
214 | |||
207 | #endif | 215 | #endif |
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 | ||
74 | void _backlight_hw_enable(bool on) | 74 | void _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); | |||
29 | void _backlight_led_off(void); | 29 | void _backlight_led_off(void); |
30 | void _backlight_hw_enable(bool on); | 30 | void _backlight_hw_enable(bool on); |
31 | 31 | ||
32 | #ifdef HAVE_LCD_SLEEP | ||
33 | void 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 | ||
131 | void power_off(void) | 131 | void 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 | |||
59 | enum lcd_status { | 87 | enum 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 | ||
107 | static long bcm_off_wait; | ||
108 | const fb_data *flash_vmcs_offset; | ||
109 | unsigned flash_vmcs_length; | ||
110 | /* This mutex exists because enabling the backlight by changing a setting | ||
111 | will cause multiple concurrent lcd_wake() calls. | ||
112 | */ | ||
113 | static 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 | */ | ||
125 | bool 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 | ||
76 | static inline void bcm_write_addr(unsigned address) | 156 | static 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 | ||
102 | static 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 |
113 | static void lcd_tick(void) | 183 | static 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 */ |
237 | void lcd_init_device(void) | 307 | void 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 | */ | ||
453 | static 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 | |||
468 | void 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 */ | ||
488 | const unsigned char bcm_bootstrapdata[] = { | ||
489 | 0xA1, 0x81, 0x91, 0x02, 0x12, 0x22, 0x72, 0x62 | ||
490 | }; | ||
491 | |||
492 | void 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 | |||
556 | void 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 | |||
586 | void 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 | ||
604 | void lcd_shutdown(void) | ||
605 | { | ||
606 | lcd_sleep(); | ||
607 | } | ||
608 | #endif /* HAVE_LCD_SHUTDOWN */ | ||
609 | #endif /* HAVE_LCD_SLEEP */ | ||