Index: firmware/export/config-ipodcolor.h =================================================================== --- firmware/export/config-ipodcolor.h (revision 20400) +++ firmware/export/config-ipodcolor.h (working copy) @@ -173,4 +173,12 @@ #define IPOD_ACCESSORY_PROTOCOL #define HAVE_SERIAL +#ifndef BOOTLOADER +/* Put LCD to sleep to save power */ +#define HAVE_LCD_SLEEP +#define HAVE_LCD_SLEEP_SETTING +/* lcd_sleep() can be used as lcd_shutdown() */ +#define HAVE_LCD_SHUTDOWN +#endif /* !BOOTLOADER */ + #endif Index: firmware/export/lcd.h =================================================================== --- firmware/export/lcd.h (revision 20400) +++ firmware/export/lcd.h (working copy) @@ -346,6 +346,11 @@ #ifdef HAVE_LCD_SLEEP /* Put the LCD into a power saving state deeper than lcd_enable(false). */ extern void lcd_sleep(void); +#ifndef HAVE_LCD_ENABLE +/* Power on LCD from deep power saving state. + * When HAVE_LCD_ENABLE is defined, lcd_enable(true) handles this. */ +void lcd_awake(void); +#endif #endif /* HAVE_LCD_SLEEP */ #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) /* Register a hook that is called when the lcd is powered and after the Index: firmware/target/arm/ipod/backlight-4g_color.c =================================================================== --- firmware/target/arm/ipod/backlight-4g_color.c (revision 20400) +++ firmware/target/arm/ipod/backlight-4g_color.c (working copy) @@ -33,9 +33,16 @@ #include "timer.h" #include "backlight.h" #include "backlight-target.h" +#ifdef HAVE_LCD_SLEEP +#include "lcd.h" +#endif void _backlight_on(void) { +#if defined(HAVE_LCD_SLEEP) && !defined(HAVE_LCD_ENABLE) + lcd_awake(); +#endif + /* brightness full */ outl(0x80000000 | (0xff << 16), 0x7000a010); Index: firmware/target/arm/ipod/backlight-nano_video.c =================================================================== --- firmware/target/arm/ipod/backlight-nano_video.c (revision 20400) +++ firmware/target/arm/ipod/backlight-nano_video.c (working copy) @@ -33,6 +33,9 @@ #include "timer.h" #include "backlight.h" #include "backlight-target.h" +#ifdef HAVE_LCD_SLEEP +#include "lcd.h" +#endif static int brightness = 1; /* 1 to 32 */ static int current_dim = 16; /* default after enabling the backlight dimmer */ Index: firmware/target/arm/ipod/backlight-target.h =================================================================== --- firmware/target/arm/ipod/backlight-target.h (revision 20400) +++ firmware/target/arm/ipod/backlight-target.h (working copy) @@ -29,10 +29,6 @@ void _backlight_led_off(void); void _backlight_hw_enable(bool on); -#ifdef HAVE_LCD_SLEEP -void lcd_awake(void); -#endif - #ifdef BOOTLOADER #define _backlight_on() do { _backlight_hw_enable(true); \ _backlight_led_on(); } while(0) Index: firmware/target/arm/ipod/video/lcd-video.c =================================================================== --- firmware/target/arm/ipod/video/lcd-video.c (revision 20400) +++ firmware/target/arm/ipod/video/lcd-video.c (working copy) @@ -30,10 +30,6 @@ #include "lcd.h" #include "kernel.h" #include "system.h" -#ifdef HAVE_LCD_SLEEP -/* Included only for lcd_awake() prototype */ -#include "backlight-target.h" -#endif /* The BCM bus width is 16 bits. But since the low address bits aren't decoded * by the chip (the 3 BCM address bits are mapped to address bits 16..18 of the Index: firmware/target/arm/ipod/lcd-color_nano.c =================================================================== --- firmware/target/arm/ipod/lcd-color_nano.c (revision 20400) +++ firmware/target/arm/ipod/lcd-color_nano.c (working copy) @@ -38,8 +38,16 @@ #define LCD_CNTL_HORIZ_RAM_ADDR_POS 0x44 #define LCD_CNTL_VERT_RAM_ADDR_POS 0x45 +#ifdef HAVE_LCD_SLEEP +static bool display_on = true; +/* This mutex exists because enabling the backlight by changing a setting + will cause multiple concurrent lcd_wake() calls. + */ +static struct mutex lcdstate_lock SHAREDBSS_ATTR; +#endif + /*** globals ***/ -int lcd_type = 1; /* 0 = "old" Color/Photo, 1 = "new" Color & Nano */ +int lcd_type = 1; /* 0 = "old" Color/Photo, 1 and 3 = simillar to HD66789R */ static inline void lcd_wait_write(void) { @@ -48,7 +56,7 @@ static void lcd_cmd_data(unsigned cmd, unsigned data) { - if (lcd_type == 0) { /* 16 bit transfers */ + if ((lcd_type & 1) == 0) { /* 16 bit transfers */ lcd_wait_write(); LCD2_PORT = LCD2_CMD_MASK | cmd; lcd_wait_write(); @@ -84,42 +92,6 @@ (void)yesno; } -/* LCD init */ -void lcd_init_device(void) -{ -#if CONFIG_LCD == LCD_IPODCOLOR - if (IPOD_HW_REVISION == 0x60000) { - lcd_type = 0; - } else { - int gpio_a01, gpio_a04; - - /* A01 */ - gpio_a01 = (GPIOA_INPUT_VAL & 0x2) >> 1; - /* A04 */ - gpio_a04 = (GPIOA_INPUT_VAL & 0x10) >> 4; - - if (((gpio_a01 << 1) | gpio_a04) == 0 || ((gpio_a01 << 1) | gpio_a04) == 2) { - lcd_type = 0; - } else { - lcd_type = 1; - } - } - if (lcd_type == 0) { - lcd_cmd_data(0xef, 0x0); - lcd_cmd_data(0x1, 0x0); - lcd_cmd_data(0x80, 0x1); - lcd_cmd_data(0x10, 0xc); - lcd_cmd_data(0x18, 0x6); - lcd_cmd_data(0x7e, 0x4); - lcd_cmd_data(0x7e, 0x5); - lcd_cmd_data(0x7f, 0x1); - } - -#elif CONFIG_LCD == LCD_IPODNANO - /* iPodLinux doesn't appear have any LCD init code for the Nano */ -#endif -} - /*** update functions ***/ #define CSUB_X 2 @@ -161,6 +133,11 @@ int h; int y0, x0, y1, x1; +#ifdef HAVE_LCD_SLEEP + if (!display_on) + return; +#endif + width = (width + 1) & ~1; /* calculate the drawing region */ @@ -177,7 +154,7 @@ #endif /* setup the drawing region */ - if (lcd_type == 0) { + if ((lcd_type & 1) == 0) { lcd_cmd_data(0x12, y0); /* start vert */ lcd_cmd_data(0x13, x0); /* start horiz */ lcd_cmd_data(0x15, y1); /* end vert */ @@ -334,6 +311,11 @@ int newx,newwidth; unsigned long *addr; +#ifdef HAVE_LCD_SLEEP + if (!display_on) + return; +#endif + /* Ensure x and width are both even - so we can read 32-bit aligned data from lcd_framebuffer */ newx=x&~1; @@ -354,7 +336,7 @@ x1 = (x0 - width) + 1; /* end horiz */ #endif /* setup the drawing region */ - if (lcd_type == 0) { + if ((lcd_type & 1) == 0) { lcd_cmd_data(0x12, y0); /* start vert */ lcd_cmd_data(0x13, x0); /* start horiz */ lcd_cmd_data(0x15, y1); /* end vert */ @@ -440,3 +422,312 @@ { lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); } + +#if defined(HAVE_LCD_SLEEP) && CONFIG_LCD == LCD_IPODCOLOR +/* The LCD init sequences are long. To save space, they are in arrays. + They are then further encapsulated for convenience and code size. + The same thing is done with sleep sequences. + */ + +/* Special operations which may be done in an LCD sequence */ +#define LCS_END 0xFF /* end of sequence (no data needed) */ +#define LCS_SLEEP 0XFE /* sleep, data is number of ticks */ +#define LCS_UDELAY 0XFD /* usleep, data is number of microseconds */ +#define LCS_CMD 0XFC /* send data as command, with no data */ +/* All other values are normal LCD commands with data */ + +/* lcd_sequence encapsulates an LCD sequence + regs points to an array which contains register indexes or LCS_ opcodes + data points to an array of register values or data for LCS_ operations + Except for LCS_END, every regs[x] requires a corresponding data[x]. + */ +struct lcd_sequence { + const unsigned char *regs; + const unsigned short *data; +}; + +void lcd_execute_sequence(const struct lcd_sequence *seq) +{ + const unsigned char *reg = seq->regs; + const unsigned short *data = seq->data; + + while (1) { + switch (*reg) { + case LCS_END: + return; + case LCS_SLEEP: + sleep(*data); + break; + case LCS_UDELAY: + udelay(*data); + break; + case LCS_CMD: + lcd_wait_write(); + LCD2_PORT = LCD2_CMD_MASK | *data; + break; + default: + lcd_cmd_data(*reg, *data); + } + reg++; + data++; + } +} + +/* The LCD descriptor groups sequences for one LCD */ +struct lcd_descriptor { + const struct lcd_sequence *sleep; + const struct lcd_sequence *awake; +}; + +/* Sequences for LCD types 0 and 2 */ + +static const unsigned char lcd0_init_r[] = { + 0xEF, 0x01, 0x80, 0x10, + 0x18, 0x7E, 0x7E, 0x7F, + LCS_END + /* or do DEV_EN |= DEV_PWM and + 0xCE, 0x02, LCS_END + */ +}; +static const unsigned short lcd0_init_d[] = { + 0x00, 0x00, 0x01, 0x0C, + 0x06, 0x04, 0x05, 0x01, + /* 0x01, 0x60 */ +}; +static const struct lcd_sequence lcd0_init = { + lcd0_init_r, lcd0_init_d +}; + +static const unsigned char lcd0_sleep_r[] = { + 0xEF, 0x80, LCS_UDELAY, 0x01, + LCS_END +}; +static const unsigned short lcd0_sleep_d[] = { + 0x00, 0x00, 1000, 0x01 +}; +static const struct lcd_sequence lcd0_sleep = { + lcd0_sleep_r, lcd0_sleep_d +}; + +static const unsigned char lcd0_awake_r[] = { + 0xEF, 0x01, 0x80, LCS_UDELAY, + 0x7E, 0x7F, LCS_END +}; +static const unsigned short lcd0_awake_d[] = { + 0x00, 0x00, 0x01, 1000, + 0x05, 0x01 +}; +static const struct lcd_sequence lcd0_awake = { + lcd0_awake_r, lcd0_awake_d +}; + +static const struct lcd_descriptor lcd0_desc = { + &lcd0_sleep, &lcd0_awake +}; + +/* Sequences for LCD type 1 */ + +static const unsigned char lcd1_awake_init_r[] = { + /* This prefix wakes the LCD. Only execute after lcd_sleep(). */ + 0x00, LCS_SLEEP, 0x10, + /* The rest of the initialization is for both waking and power-on */ + 0x11, 0x12, 0x13, 0x10, + 0x10, 0x11, 0x12, LCS_SLEEP, + 0x13, 0x10, LCS_SLEEP, 0x01, + 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x21, + 0x22, 0x23, 0x24, 0x30, + 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, + 0x39, 0x40, 0x41, 0x42, + 0x43, 0x44, 0x45, 0x10, + 0x07, LCS_SLEEP, 0x07, 0x07, + LCS_SLEEP, 0x07, LCS_END +}; +static const unsigned short lcd1_awake_init_d[] = { + 0x0001, 10*HZ/1000, 0x0504, + + 0x0004, 0x0004, 0x100F, 0x0004, + 0x0064, 0x0204, 0x0014, 40*HZ/1000, + 0x300F, 0x0560, 100*HZ/1000, 0x0A1B, + 0x0700, 0x0028, 0x0000, 0x0000, + 0x0503, 0x0010, 0x0008, 0x0000, + 0x8611, 0x0611, 0x0016, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0401, 0x0305, 0x0000, 0x0104, + 0x0603, 0x0707, 0x0000, 0x0F01, + 0x0309, 0x0002, 0x00EC, 0xDF04, + 0xFFFF, 0xAF00, 0xDB00, 0x6560, + 0x0205, 40*HZ/1000, 0x0225, 0x0227, + 40*HZ/1000, 0x0237 +}; +static const struct lcd_sequence lcd1_awake = { + lcd1_awake_init_r, lcd1_awake_init_d +}; + +static const unsigned char lcd1_sleep_r[] = { + 0x07, LCS_SLEEP, 0x07, LCS_SLEEP, + 0x07, 0x10, LCS_SLEEP, 0x10, + LCS_SLEEP, 0x10, LCS_END +}; +static const unsigned short lcd1_sleep_d[] = { + 0x0236, 40*HZ/1000, 0x0226, 40*HZ/1000, + 0x0204, 0x7574, 200*HZ/1000, 0x7504, + 50*HZ/1000, 0x0501 +}; +static const struct lcd_sequence lcd1_sleep = { + lcd1_sleep_r, lcd1_sleep_d +}; + +static const struct lcd_descriptor lcd1_desc = { + &lcd1_sleep, &lcd1_awake +}; + +/* Sequences for LCD type 3 */ + +static const unsigned char lcd3_awake_init_r[] = { + /* This prefix wakes the LCD. Only execute after lcd_sleep(). */ + 0x00, 0x10, + /* The rest of the initialization is for both waking and power-on */ + 0x00, LCS_SLEEP, LCS_CMD, LCS_CMD, + LCS_CMD, LCS_CMD, 0x07, LCS_SLEEP, + 0x59, 0x01, 0x02, 0x03, + 0x08, 0x09, 0x0B, 0x0C, + 0x0D, 0x0E, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x44, 0x45, 0x42, + 0x21, 0x50, 0x51, 0x52, + 0x53, 0x58, 0x5A, 0x11, + 0x12, 0x13, 0x10, LCS_SLEEP, + 0x10, LCS_SLEEP, 0x10, 0x07, + LCS_UDELAY, 0x07, 0x5B, 0x59, + LCS_SLEEP, 0x59, LCS_SLEEP, 0x59, + LCS_SLEEP, 0x59, LCS_SLEEP, LCS_END +}; +static const unsigned short lcd3_awake_init_d[] = { + 0x0001, 0x0300, + + 0x0001, 10*HZ/1000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x4005, 20*HZ/1000, + 0x0000, 0x001B, 0x0700, 0x0230, + 0x0402, 0x0000, 0x0000, 0x0000, + 0x0409, 0x0409, 0x0000, 0x0100, + 0x0104, 0x0400, 0x0306, 0x0706, + 0x0707, 0x0004, 0x0000, 0x0000, + 0x0001, 0xAF00, 0xDB00, 0xDB00, + 0x0000, 0x0000, 0x0E00, 0x0D01, + 0x0000, 0x0000, 0x0E01, 0x0812, + 0x0003, 0x0909, 0x0040, 40*HZ/1000, + 0x0340, 60*HZ/1000, 0x3340, 0x4007, + 300, 0x4017, 0x0000, 0x0011, + 20*HZ/1000, 0x0011, 20*HZ/1000, 0x0071, + 20*HZ/1000, 0x03F5, 20*HZ/1000 +}; +static const struct lcd_sequence lcd3_awake = { + lcd3_awake_init_r, lcd3_awake_init_d +}; + +static const unsigned char lcd3_sleep_r[] = { + 0x07, LCS_SLEEP, 0x59, LCS_SLEEP, + 0x59, LCS_SLEEP, 0x59, LCS_SLEEP, + 0x10, LCS_SLEEP, 0x10, LCS_SLEEP, + 0x10, 0x59, 0x07, 0x10, + LCS_END +}; +static const unsigned short lcd3_sleep_d[] = { + 0x4016, 20*HZ/1000, 0x0011, 20*HZ/1000, + 0x0003, 20*HZ/1000, 0x0002, 20*HZ/1000, + 0x6360, 200*HZ/1000, 0x6300, 50*HZ/1000, + 0x0300, 0x0000, 0x4004, 0x0301 +}; +static const struct lcd_sequence lcd3_sleep = { + lcd3_sleep_r, lcd3_sleep_d +}; + +static const struct lcd_descriptor lcd3_desc = { + &lcd3_sleep, &lcd3_awake +}; + +/* lcd_types contains descriptors for all LCDs */ +static const struct lcd_descriptor * const lcd_types[] = { + &lcd0_desc, + &lcd1_desc, + &lcd0_desc, + &lcd3_desc +}; + +void lcd_sleep(void) { + mutex_lock(&lcdstate_lock); + if (display_on == true) { + display_on = false; + + lcd_execute_sequence(lcd_types[lcd_type]->sleep); + } + mutex_unlock(&lcdstate_lock); +} + +void lcd_awake(void) { + mutex_lock(&lcdstate_lock); + if (display_on == false) { + lcd_execute_sequence(lcd_types[lcd_type]->awake); + + display_on = true; + + lcd_update(); /* Resync display */ + lcd_activation_call_hook(); + + /* Wait here for LCD to be refreshed. It is not known if + this is long enough for all LCD types. If it is too + short, you will see a white flash when waking the LCD. + */ + sleep(HZ/50); + } + mutex_unlock(&lcdstate_lock); +} + +bool lcd_active(void) { + return display_on; +} + +#ifdef HAVE_LCD_SHUTDOWN +void lcd_shutdown(void) { + lcd_sleep(); +} +#endif +#endif /* HAVE_LCD_SLEEP */ + +/* LCD init */ +void lcd_init_device(void) +{ +#ifdef HAVE_LCD_SLEEP + mutex_init(&lcdstate_lock); +#endif + +#if CONFIG_LCD == LCD_IPODCOLOR + if (IPOD_HW_REVISION == 0x60000) { + lcd_type = 0; + } else { + lcd_type = (GPIOA_INPUT_VAL & 0x2) | ((GPIOA_INPUT_VAL & 0x10) >> 4); + } + if ((lcd_type & 1) == 0) { +#ifdef HAVE_LCD_SLEEP + /* This is equivalent to the #else. It just uses less memory. */ + lcd_execute_sequence(&lcd0_init); +#else + lcd_cmd_data(0xef, 0x0); + lcd_cmd_data(0x1, 0x0); + lcd_cmd_data(0x80, 0x1); + lcd_cmd_data(0x10, 0xc); + lcd_cmd_data(0x18, 0x6); + lcd_cmd_data(0x7e, 0x4); + lcd_cmd_data(0x7e, 0x5); + lcd_cmd_data(0x7f, 0x1); +#endif /* !HAVE_LCD_SLEEP */ + } + +#elif CONFIG_LCD == LCD_IPODNANO + /* iPodLinux doesn't appear have any LCD init code for the Nano */ +#endif +}