diff options
Diffstat (limited to 'firmware/target/arm/ipod')
-rw-r--r-- | firmware/target/arm/ipod/video/lcd-video.c | 113 |
1 files changed, 62 insertions, 51 deletions
diff --git a/firmware/target/arm/ipod/video/lcd-video.c b/firmware/target/arm/ipod/video/lcd-video.c index da3011887c..e62928724e 100644 --- a/firmware/target/arm/ipod/video/lcd-video.c +++ b/firmware/target/arm/ipod/video/lcd-video.c | |||
@@ -8,7 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * LCD driver for iPod Video | 10 | * LCD driver for iPod Video |
11 | * | 11 | * |
12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ | 12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ |
13 | * Adapted for Rockbox in December 2005 | 13 | * Adapted for Rockbox in December 2005 |
14 | * | 14 | * |
@@ -56,7 +56,7 @@ | |||
56 | 56 | ||
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 */ | 60 | /* An LCD update command done while the LCD is off needs >~ 200ms */ |
61 | #define BCM_LCDINIT_TIMEOUT (HZ/2) | 61 | #define BCM_LCDINIT_TIMEOUT (HZ/2) |
62 | 62 | ||
@@ -81,19 +81,21 @@ | |||
81 | #define BCMCMD_LCD_UPDATERECT BCM_CMD(5) | 81 | #define BCMCMD_LCD_UPDATERECT BCM_CMD(5) |
82 | #define BCMCMD_LCD_SLEEP BCM_CMD(8) | 82 | #define BCMCMD_LCD_SLEEP BCM_CMD(8) |
83 | /* BCM_CMD(12) involved in shutdown */ | 83 | /* BCM_CMD(12) involved in shutdown */ |
84 | /* Macrovision analog copy prevention is on by default on TV output. | 84 | /* Macrovision analog copy prevention is on by default on TV output. |
85 | Execute this command after enabling TV out to turn it off. | 85 | Execute this command after enabling TV out to turn it off. |
86 | */ | 86 | */ |
87 | #define BCMCMD_TV_MVOFF BCM_CMD(14) | 87 | #define BCMCMD_TV_MVOFF BCM_CMD(14) |
88 | 88 | ||
89 | enum lcd_status { | 89 | enum lcd_status |
90 | { | ||
90 | LCD_IDLE, | 91 | LCD_IDLE, |
91 | LCD_INITIAL, | 92 | LCD_INITIAL, |
92 | LCD_NEED_UPDATE, | 93 | LCD_NEED_UPDATE, |
93 | LCD_UPDATING | 94 | LCD_UPDATING |
94 | }; | 95 | }; |
95 | 96 | ||
96 | struct { | 97 | struct |
98 | { | ||
97 | long update_timeout; /* also used to ensure BCM stays off for >= 50 ms */ | 99 | long update_timeout; /* also used to ensure BCM stays off for >= 50 ms */ |
98 | enum lcd_status state; | 100 | enum lcd_status state; |
99 | bool blocked; | 101 | bool blocked; |
@@ -125,7 +127,7 @@ static struct mutex lcdstate_lock SHAREDBSS_ATTR; | |||
125 | Based on part of FS#6721. This may belong elsewhere. | 127 | Based on part of FS#6721. This may belong elsewhere. |
126 | (BCM initialization uploads the vmcs section to the BCM.) | 128 | (BCM initialization uploads the vmcs section to the BCM.) |
127 | */ | 129 | */ |
128 | bool flash_get_section(const unsigned int imageid, | 130 | bool flash_get_section(const unsigned int imageid, |
129 | void **offset, | 131 | void **offset, |
130 | unsigned int *length) | 132 | unsigned int *length) |
131 | { | 133 | { |
@@ -134,7 +136,8 @@ bool flash_get_section(const unsigned int imageid, | |||
134 | unsigned long checksum; | 136 | unsigned long checksum; |
135 | 137 | ||
136 | /* Find the image in the directory */ | 138 | /* Find the image in the directory */ |
137 | while (1) { | 139 | while (1) |
140 | { | ||
138 | if (p[0] != ROM_ID('f','l','s','h')) | 141 | if (p[0] != ROM_ID('f','l','s','h')) |
139 | return false; | 142 | return false; |
140 | if (p[1] == imageid) | 143 | if (p[1] == imageid) |
@@ -148,7 +151,8 @@ bool flash_get_section(const unsigned int imageid, | |||
148 | /* Verify checksum. Probably unnecessary, but it's fast. */ | 151 | /* Verify checksum. Probably unnecessary, but it's fast. */ |
149 | checksum = 0; | 152 | checksum = 0; |
150 | csend = (unsigned char *)(ROM_BASE + p[3] + p[4]); | 153 | csend = (unsigned char *)(ROM_BASE + p[3] + p[4]); |
151 | for(csp = (unsigned char *)(ROM_BASE + p[3]); csp < csend; csp++) { | 154 | for(csp = (unsigned char *)(ROM_BASE + p[3]); csp < csend; csp++) |
155 | { | ||
152 | checksum += *csp; | 156 | checksum += *csp; |
153 | } | 157 | } |
154 | 158 | ||
@@ -202,8 +206,8 @@ static void lcd_tick(void) | |||
202 | { | 206 | { |
203 | unsigned data = bcm_read32(BCMA_COMMAND); | 207 | unsigned data = bcm_read32(BCMA_COMMAND); |
204 | bool bcm_is_busy = (data == BCMCMD_LCD_UPDATE || data == 0xFFFF); | 208 | bool bcm_is_busy = (data == BCMCMD_LCD_UPDATE || data == 0xFFFF); |
205 | 209 | ||
206 | if (((lcd_state.state == LCD_NEED_UPDATE) && !bcm_is_busy) | 210 | if (((lcd_state.state == LCD_NEED_UPDATE) && !bcm_is_busy) |
207 | /* Update requested and BCM is no longer busy. */ | 211 | /* Update requested and BCM is no longer busy. */ |
208 | || (TIME_AFTER(current_tick, lcd_state.update_timeout) && bcm_is_busy)) | 212 | || (TIME_AFTER(current_tick, lcd_state.update_timeout) && bcm_is_busy)) |
209 | /* BCM still busy after timeout, i.e. stalled. */ | 213 | /* BCM still busy after timeout, i.e. stalled. */ |
@@ -257,7 +261,7 @@ static void lcd_unblock_and_update(void) | |||
257 | #endif | 261 | #endif |
258 | data = bcm_read32(BCMA_COMMAND); | 262 | data = bcm_read32(BCMA_COMMAND); |
259 | bcm_is_busy = (data == BCMCMD_LCD_UPDATE || data == 0xFFFF); | 263 | bcm_is_busy = (data == BCMCMD_LCD_UPDATE || data == 0xFFFF); |
260 | 264 | ||
261 | if (!bcm_is_busy || (lcd_state.state == LCD_INITIAL) || | 265 | if (!bcm_is_busy || (lcd_state.state == LCD_INITIAL) || |
262 | TIME_AFTER(current_tick, lcd_state.update_timeout)) | 266 | TIME_AFTER(current_tick, lcd_state.update_timeout)) |
263 | { | 267 | { |
@@ -275,7 +279,7 @@ static void lcd_unblock_and_update(void) | |||
275 | lcd_state.state = LCD_NEED_UPDATE; /* Post update request */ | 279 | lcd_state.state = LCD_NEED_UPDATE; /* Post update request */ |
276 | } | 280 | } |
277 | lcd_state.blocked = false; | 281 | lcd_state.blocked = false; |
278 | 282 | ||
279 | #if NUM_CORES > 1 | 283 | #if NUM_CORES > 1 |
280 | corelock_unlock(&lcd_state.cl); | 284 | corelock_unlock(&lcd_state.cl); |
281 | #endif | 285 | #endif |
@@ -289,7 +293,7 @@ static void lcd_unblock_and_update(void) | |||
289 | static void lcd_unblock_and_update(void) | 293 | static void lcd_unblock_and_update(void) |
290 | { | 294 | { |
291 | unsigned data; | 295 | unsigned data; |
292 | 296 | ||
293 | if (lcd_state.state != LCD_INITIAL) | 297 | if (lcd_state.state != LCD_INITIAL) |
294 | { | 298 | { |
295 | data = bcm_read32(BCMA_COMMAND); | 299 | data = bcm_read32(BCMA_COMMAND); |
@@ -333,7 +337,7 @@ void lcd_init_device(void) | |||
333 | the BCM. None of it is changed when shutting down the BCM. | 337 | the BCM. None of it is changed when shutting down the BCM. |
334 | */ | 338 | */ |
335 | GPO32_ENABLE |= 0xC000; | 339 | GPO32_ENABLE |= 0xC000; |
336 | GPIO_CLEAR_BITWISE(GPIOC_ENABLE, 0x80); | 340 | GPIO_CLEAR_BITWISE(GPIOC_ENABLE, 0x80); |
337 | /* This pin is used for BCM interrupts */ | 341 | /* This pin is used for BCM interrupts */ |
338 | GPIOC_ENABLE |= 0x40; | 342 | GPIOC_ENABLE |= 0x40; |
339 | GPIOC_OUTPUT_EN &= ~0x40; | 343 | GPIOC_OUTPUT_EN &= ~0x40; |
@@ -346,11 +350,12 @@ void lcd_init_device(void) | |||
346 | corelock_init(&lcd_state.cl); | 350 | corelock_init(&lcd_state.cl); |
347 | #endif | 351 | #endif |
348 | #ifdef HAVE_LCD_SLEEP | 352 | #ifdef HAVE_LCD_SLEEP |
349 | if (!flash_get_section(ROM_ID('v', 'm', 'c', 's'), | 353 | if (!flash_get_section(ROM_ID('v', 'm', 'c', 's'), |
350 | (void **)(&flash_vmcs_offset), &flash_vmcs_length)) | 354 | (void **)(&flash_vmcs_offset), &flash_vmcs_length)) |
351 | /* BCM cannot be shut down because firmware wasn't found */ | 355 | /* BCM cannot be shut down because firmware wasn't found */ |
352 | flash_vmcs_length = 0; | 356 | flash_vmcs_length = 0; |
353 | else { | 357 | else |
358 | { | ||
354 | /* lcd_write_data needs an even number of 16 bit values */ | 359 | /* lcd_write_data needs an even number of 16 bit values */ |
355 | flash_vmcs_length = ((flash_vmcs_length + 3) >> 1) & ~1; | 360 | flash_vmcs_length = ((flash_vmcs_length + 3) >> 1) & ~1; |
356 | } | 361 | } |
@@ -358,15 +363,15 @@ void lcd_init_device(void) | |||
358 | wakeup_init(&(lcd_state.initwakeup)); | 363 | wakeup_init(&(lcd_state.initwakeup)); |
359 | lcd_state.waking = false; | 364 | lcd_state.waking = false; |
360 | 365 | ||
361 | if (GPO32_VAL & 0x4000) | 366 | if (GPO32_VAL & 0x4000) |
362 | { | 367 | { |
363 | /* BCM is powered. Assume it is initialized. */ | 368 | /* BCM is powered. Assume it is initialized. */ |
364 | lcd_state.display_on = true; | 369 | lcd_state.display_on = true; |
365 | tick_add_task(&lcd_tick); | 370 | tick_add_task(&lcd_tick); |
366 | } | 371 | } |
367 | else | 372 | else |
368 | { | 373 | { |
369 | /* BCM is not powered, so it needs to be initialized. | 374 | /* BCM is not powered, so it needs to be initialized. |
370 | This can only happen when loading Rockbox via ROLO. | 375 | This can only happen when loading Rockbox via ROLO. |
371 | */ | 376 | */ |
372 | lcd_state.update_timeout = current_tick; | 377 | lcd_state.update_timeout = current_tick; |
@@ -375,7 +380,7 @@ void lcd_init_device(void) | |||
375 | } | 380 | } |
376 | #else /* !HAVE_LCD_SLEEP */ | 381 | #else /* !HAVE_LCD_SLEEP */ |
377 | tick_add_task(&lcd_tick); | 382 | tick_add_task(&lcd_tick); |
378 | #endif | 383 | #endif |
379 | #endif /* !BOOTLOADER */ | 384 | #endif /* !BOOTLOADER */ |
380 | } | 385 | } |
381 | 386 | ||
@@ -396,7 +401,7 @@ void lcd_update_rect(int x, int y, int width, int height) | |||
396 | width = LCD_WIDTH - x; | 401 | width = LCD_WIDTH - x; |
397 | if (y + height >= LCD_HEIGHT) | 402 | if (y + height >= LCD_HEIGHT) |
398 | height = LCD_HEIGHT - y; | 403 | height = LCD_HEIGHT - y; |
399 | 404 | ||
400 | if ((width <= 0) || (height <= 0)) | 405 | if ((width <= 0) || (height <= 0)) |
401 | return; /* Nothing left to do. */ | 406 | return; /* Nothing left to do. */ |
402 | 407 | ||
@@ -404,7 +409,7 @@ void lcd_update_rect(int x, int y, int width, int height) | |||
404 | * writes and would just ignore them. */ | 409 | * writes and would just ignore them. */ |
405 | width = (width + (x & 1) + 1) & ~1; | 410 | width = (width + (x & 1) + 1) & ~1; |
406 | x &= ~1; | 411 | x &= ~1; |
407 | 412 | ||
408 | /* Prevent the tick from triggering BCM updates while we're writing. */ | 413 | /* Prevent the tick from triggering BCM updates while we're writing. */ |
409 | lcd_block_tick(); | 414 | lcd_block_tick(); |
410 | 415 | ||
@@ -442,7 +447,7 @@ extern void lcd_write_yuv420_lines(unsigned char const * const src[3], | |||
442 | unsigned bcmaddr, | 447 | unsigned bcmaddr, |
443 | int width, | 448 | int width, |
444 | int stride); | 449 | int stride); |
445 | 450 | ||
446 | /* Performance function to blit a YUV bitmap directly to the LCD */ | 451 | /* Performance function to blit a YUV bitmap directly to the LCD */ |
447 | void lcd_blit_yuv(unsigned char * const src[3], | 452 | void lcd_blit_yuv(unsigned char * const src[3], |
448 | int src_x, int src_y, int stride, | 453 | int src_x, int src_y, int stride, |
@@ -478,24 +483,26 @@ void lcd_blit_yuv(unsigned char * const src[3], | |||
478 | yuv_src[0] += stride << 1; | 483 | yuv_src[0] += stride << 1; |
479 | yuv_src[1] += stride >> 1; /* Skip down one chroma line */ | 484 | yuv_src[1] += stride >> 1; /* Skip down one chroma line */ |
480 | yuv_src[2] += stride >> 1; | 485 | yuv_src[2] += stride >> 1; |
481 | } | 486 | } |
482 | while (--height > 0); | 487 | while (--height > 0); |
483 | 488 | ||
484 | lcd_unblock_and_update(); | 489 | lcd_unblock_and_update(); |
485 | } | 490 | } |
486 | 491 | ||
487 | #ifdef HAVE_LCD_SLEEP | 492 | #ifdef HAVE_LCD_SLEEP |
488 | /* Executes a BCM command immediately and waits for it to complete. | 493 | /* Executes a BCM command immediately and waits for it to complete. |
489 | Other BCM commands (eg. LCD updates or lcd_tick) must not interfere. | 494 | Other BCM commands (eg. LCD updates or lcd_tick) must not interfere. |
490 | */ | 495 | */ |
491 | static void bcm_command(unsigned cmd) { | 496 | static void bcm_command(unsigned cmd) |
497 | { | ||
492 | unsigned status; | 498 | unsigned status; |
493 | 499 | ||
494 | bcm_write32(BCMA_COMMAND, cmd); | 500 | bcm_write32(BCMA_COMMAND, cmd); |
495 | 501 | ||
496 | BCM_CONTROL = 0x31; | 502 | BCM_CONTROL = 0x31; |
497 | 503 | ||
498 | while (1) { | 504 | while (1) |
505 | { | ||
499 | status = bcm_read32(BCMA_COMMAND); | 506 | status = bcm_read32(BCMA_COMMAND); |
500 | if (status != cmd && status != 0xFFFF) | 507 | if (status != cmd && status != 0xFFFF) |
501 | break; | 508 | break; |
@@ -507,7 +514,7 @@ void bcm_powerdown(void) | |||
507 | { | 514 | { |
508 | bcm_write32(0x10001400, bcm_read32(0x10001400) & ~0xF0); | 515 | bcm_write32(0x10001400, bcm_read32(0x10001400) & ~0xF0); |
509 | 516 | ||
510 | /* Blanks the LCD and decreases power consumption | 517 | /* Blanks the LCD and decreases power consumption |
511 | below what clearing the LCD would achieve. | 518 | below what clearing the LCD would achieve. |
512 | Executing an LCD update command wakes it. | 519 | Executing an LCD update command wakes it. |
513 | */ | 520 | */ |
@@ -523,17 +530,18 @@ void bcm_powerdown(void) | |||
523 | } | 530 | } |
524 | 531 | ||
525 | /* Data written to BCM_CONTROL and BCM_ALT_CONTROL */ | 532 | /* Data written to BCM_CONTROL and BCM_ALT_CONTROL */ |
526 | const unsigned char bcm_bootstrapdata[] = { | 533 | const unsigned char bcm_bootstrapdata[] = |
534 | { | ||
527 | 0xA1, 0x81, 0x91, 0x02, 0x12, 0x22, 0x72, 0x62 | 535 | 0xA1, 0x81, 0x91, 0x02, 0x12, 0x22, 0x72, 0x62 |
528 | }; | 536 | }; |
529 | 537 | ||
530 | void bcm_init(void) | 538 | void bcm_init(void) |
531 | { | 539 | { |
532 | int i; | 540 | int i; |
533 | 541 | ||
534 | /* Power up BCM */ | 542 | /* Power up BCM */ |
535 | GPO32_VAL |= 0x4000; | 543 | GPO32_VAL |= 0x4000; |
536 | sleep(HZ/20); | 544 | sleep(HZ/20); |
537 | 545 | ||
538 | /* Bootstrap stage 1 */ | 546 | /* Bootstrap stage 1 */ |
539 | 547 | ||
@@ -543,7 +551,7 @@ void bcm_init(void) | |||
543 | /* Interrupt-related code for future use | 551 | /* Interrupt-related code for future use |
544 | GPIOC_INT_LEV |= 0x40; | 552 | GPIOC_INT_LEV |= 0x40; |
545 | GPIOC_INT_EN |= 0x40; | 553 | GPIOC_INT_EN |= 0x40; |
546 | CPU_HI_INT_EN |= 0x40000; | 554 | CPU_HI_INT_EN |= 0x40000; |
547 | */ | 555 | */ |
548 | 556 | ||
549 | /* Bootstrap stage 2 */ | 557 | /* Bootstrap stage 2 */ |
@@ -551,21 +559,23 @@ void bcm_init(void) | |||
551 | while (BCM_ALT_CONTROL & 0x80); | 559 | while (BCM_ALT_CONTROL & 0x80); |
552 | while (!(BCM_ALT_CONTROL & 0x40)); | 560 | while (!(BCM_ALT_CONTROL & 0x40)); |
553 | 561 | ||
554 | for (i = 0; i < 8; i++) { | 562 | for (i = 0; i < 8; i++) |
563 | { | ||
555 | BCM_CONTROL = bcm_bootstrapdata[i]; | 564 | BCM_CONTROL = bcm_bootstrapdata[i]; |
556 | } | 565 | } |
557 | 566 | ||
558 | for (i = 3; i < 8; i++) { | 567 | for (i = 3; i < 8; i++) |
568 | { | ||
559 | BCM_ALT_CONTROL = bcm_bootstrapdata[i]; | 569 | BCM_ALT_CONTROL = bcm_bootstrapdata[i]; |
560 | } | 570 | } |
561 | 571 | ||
562 | while ((BCM_RD_ADDR & 1) == 0 || (BCM_ALT_RD_ADDR & 1) == 0); | 572 | while ((BCM_RD_ADDR & 1) == 0 || (BCM_ALT_RD_ADDR & 1) == 0); |
563 | 573 | ||
564 | (void)BCM_WR_ADDR; | 574 | (void)BCM_WR_ADDR; |
565 | (void)BCM_ALT_WR_ADDR; | 575 | (void)BCM_ALT_WR_ADDR; |
566 | 576 | ||
567 | /* Bootstrap stage 3: upload firmware */ | 577 | /* Bootstrap stage 3: upload firmware */ |
568 | 578 | ||
569 | while (BCM_ALT_CONTROL & 0x80); | 579 | while (BCM_ALT_CONTROL & 0x80); |
570 | while (!(BCM_ALT_CONTROL & 0x40)); | 580 | while (!(BCM_ALT_CONTROL & 0x40)); |
571 | 581 | ||
@@ -575,12 +585,12 @@ void bcm_init(void) | |||
575 | 585 | ||
576 | bcm_write32(BCMA_COMMAND, 0); | 586 | bcm_write32(BCMA_COMMAND, 0); |
577 | bcm_write32(0x10000C00, 0xC0000000); | 587 | bcm_write32(0x10000C00, 0xC0000000); |
578 | 588 | ||
579 | while (!(bcm_read32(0x10000C00) & 1)); | 589 | while (!(bcm_read32(0x10000C00) & 1)); |
580 | 590 | ||
581 | bcm_write32(0x10000C00, 0); | 591 | bcm_write32(0x10000C00, 0); |
582 | bcm_write32(0x10000400, 0xA5A50002); | 592 | bcm_write32(0x10000400, 0xA5A50002); |
583 | 593 | ||
584 | while (bcm_read32(BCMA_COMMAND) == 0) | 594 | while (bcm_read32(BCMA_COMMAND) == 0) |
585 | yield(); | 595 | yield(); |
586 | 596 | ||
@@ -605,7 +615,7 @@ void lcd_awake(void) | |||
605 | lcd_update(); | 615 | lcd_update(); |
606 | lcd_state.update_timeout = current_tick + BCM_LCDINIT_TIMEOUT; | 616 | lcd_state.update_timeout = current_tick + BCM_LCDINIT_TIMEOUT; |
607 | 617 | ||
608 | /* Wait for end of first LCD update, so LCD isn't white | 618 | /* Wait for end of first LCD update, so LCD isn't white |
609 | when the backlight turns on. | 619 | when the backlight turns on. |
610 | */ | 620 | */ |
611 | lcd_state.waking = true; | 621 | lcd_state.waking = true; |
@@ -617,16 +627,17 @@ void lcd_awake(void) | |||
617 | mutex_unlock(&lcdstate_lock); | 627 | mutex_unlock(&lcdstate_lock); |
618 | } | 628 | } |
619 | 629 | ||
620 | void lcd_sleep(void) | 630 | void lcd_sleep(void) |
621 | { | 631 | { |
622 | mutex_lock(&lcdstate_lock); | 632 | mutex_lock(&lcdstate_lock); |
623 | if (lcd_state.display_on && flash_vmcs_length != 0) { | 633 | if (lcd_state.display_on && flash_vmcs_length != 0) |
634 | { | ||
624 | lcd_state.display_on = false; | 635 | lcd_state.display_on = false; |
625 | 636 | ||
626 | /* Wait for BCM to finish work */ | 637 | /* Wait for BCM to finish work */ |
627 | while (lcd_state.state != LCD_INITIAL && lcd_state.state != LCD_IDLE) | 638 | while (lcd_state.state != LCD_INITIAL && lcd_state.state != LCD_IDLE) |
628 | yield(); | 639 | yield(); |
629 | 640 | ||
630 | tick_remove_task(&lcd_tick); | 641 | tick_remove_task(&lcd_tick); |
631 | bcm_powerdown(); | 642 | bcm_powerdown(); |
632 | 643 | ||
@@ -642,7 +653,7 @@ bool lcd_active(void) | |||
642 | } | 653 | } |
643 | 654 | ||
644 | #ifdef HAVE_LCD_SHUTDOWN | 655 | #ifdef HAVE_LCD_SHUTDOWN |
645 | void lcd_shutdown(void) | 656 | void lcd_shutdown(void) |
646 | { | 657 | { |
647 | lcd_sleep(); | 658 | lcd_sleep(); |
648 | } | 659 | } |