/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2006 by Linus Nielsen Feltzing * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "config.h" #include "cpu.h" #include "system.h" #include "backlight-target.h" #include "backlight.h" #include "lcd.h" #include "sc606-meg-fx.h" #include "power.h" #define FLICKER_PERIOD 15 #define BUTTONLIGHT_MENU (SC606_LED_B1) #define BUTTONLIGHT_ALL (SC606_LED_B1 | SC606_LED_B2 | SC606_LED_C1 | SC606_LED_C2) static void led_control_service(void); static unsigned short backlight_brightness; static unsigned short backlight_current; static unsigned short backlight_target; static unsigned short time_til_fade; static unsigned short fade_interval; static unsigned short initial_tick_delay; static unsigned char backlight_leds; static enum backlight_states { BACKLIGHT_CONTROL_IDLE, BACKLIGHT_CONTROL_OFF, BACKLIGHT_CONTROL_ON, BACKLIGHT_CONTROL_SET, BACKLIGHT_CONTROL_FADE_OFF, BACKLIGHT_CONTROL_FADE_ON, BACKLIGHT_CONTROL_FADE_ON_FROM_OFF } backlight_control; enum buttonlight_states { /* turn button lights off */ BUTTONLIGHT_MODE_OFF_ENTRY, BUTTONLIGHT_MODE_OFF, /* turns button lights on to setting */ BUTTONLIGHT_MODE_ON_ENTRY, BUTTONLIGHT_MODE_ON, /* turns button lights on to minimum */ BUTTONLIGHT_MODE_FAINT_ENTRY, BUTTONLIGHT_MODE_FAINT, /* allows button lights to flicker when triggered */ BUTTONLIGHT_MODE_FLICKER_ENTRY, BUTTONLIGHT_MODE_FLICKER, BUTTONLIGHT_MODE_FLICKERING, /* button lights solid */ BUTTONLIGHT_MODE_SOLID_ENTRY, BUTTONLIGHT_MODE_SOLID, /* button light charing */ BUTTONLIGHT_MODE_CHARGING_ENTRY, BUTTONLIGHT_MODE_CHARGING, BUTTONLIGHT_MODE_CHARGING_WAIT, /* internal use only */ BUTTONLIGHT_HELPER_SET, BUTTONLIGHT_HELPER_SET_FINAL, BUTTONLIGHT_MODE_STOP, /* buttonlights follow the backlight settings */ BUTTONLIGHT_MODE_FOLLOW_ENTRY, BUTTONLIGHT_MODE_FOLLOW, }; static char buttonlight_leds; static unsigned short buttonlight_setting; static unsigned short buttonlight_current; static unsigned char buttonlight_selected; static enum buttonlight_states buttonlight_state; static enum buttonlight_states buttonlight_saved_state; static unsigned short buttonlight_flickering; static unsigned short buttonlight_trigger_now; static unsigned short buttonlight_trigger_brightness; static unsigned short charging_led_index; static unsigned short buttonlight_charging_counter; #define CHARGING_LED_COUNT 60 unsigned char charging_leds[] = { 0x00, 0x20, 0x38, 0x3C }; void __backlight_init(void) { backlight_control = BACKLIGHT_CONTROL_IDLE; backlight_current = DEFAULT_BRIGHTNESS_SETTING; buttonlight_state = BUTTONLIGHT_MODE_OFF; buttonlight_selected = 0x04; /* delay 5 seconds before any fading */ initial_tick_delay = 5000; /* put the led control on the tick list */ tick_add_task(led_control_service); } void __backlight_on(void) { /* now go turn the backlight on */ backlight_control = BACKLIGHT_CONTROL_ON; } void __backlight_off(void) { backlight_control = BACKLIGHT_CONTROL_OFF; } /* Assumes that the backlight has been initialized */ void __backlight_set_brightness(int brightness) { /* stop the interrupt from messing us up */ backlight_control = BACKLIGHT_CONTROL_IDLE; backlight_brightness = brightness + 1; /* only set the brightness if it is different from the current */ if (backlight_brightness != backlight_current) { backlight_control = BACKLIGHT_CONTROL_SET; } } /* only works if the buttonlight mode is set to triggered mode */ void __buttonlight_trigger(void) { buttonlight_trigger_now = 1; } /* map the mode from the command into the state machine entries */ void __buttonlight_mode(enum buttonlight_mode mode, enum buttonlight_selection selection, unsigned short brightness) { /* choose stop to setup mode */ buttonlight_state = BUTTONLIGHT_MODE_STOP; /* clip brightness */ if (brightness > MAX_BRIGHTNESS_SETTING) { brightness = MAX_BRIGHTNESS_SETTING; } brightness++; /* Select which LEDs to use */ switch (selection) { case BUTTONLIGHT_LED_ALL: buttonlight_selected = BUTTONLIGHT_ALL; break; case BUTTONLIGHT_LED_MENU: buttonlight_selected = BUTTONLIGHT_MENU; break; } /* which mode to use */ switch (mode) { case BUTTONLIGHT_OFF: buttonlight_state = BUTTONLIGHT_MODE_OFF_ENTRY; break; case BUTTONLIGHT_ON: buttonlight_trigger_brightness = brightness; buttonlight_state = BUTTONLIGHT_MODE_ON_ENTRY; break; /* faint is just a quick way to set ON to 1 */ case BUTTONLIGHT_FAINT: buttonlight_trigger_brightness = 1; buttonlight_state = BUTTONLIGHT_MODE_ON_ENTRY; break; case BUTTONLIGHT_FLICKER: buttonlight_trigger_brightness = brightness; buttonlight_state = BUTTONLIGHT_MODE_FLICKER_ENTRY; break; case BUTTONLIGHT_SIGNAL: buttonlight_trigger_brightness = brightness; buttonlight_state = BUTTONLIGHT_MODE_SOLID_ENTRY; break; case BUTTONLIGHT_FOLLOW: buttonlight_state = BUTTONLIGHT_MODE_FOLLOW_ENTRY; break; case BUTTONLIGHT_CHARGING: buttonlight_state = BUTTONLIGHT_MODE_CHARGING_ENTRY; break; default: return; /* unknown mode */ } } /* * The button lights have 'modes' of operation. Each mode must setup and * execute its own operation - taking care that this is all done in an ISR. * */ /* led_control_service runs in interrupt context - be brief! * This service is called once per interrupt timer tick - 100 times a second. * * There should be at most only one i2c operation per call - if more are need * the calls should be spread across calls. * * Putting all led servicing in one thread means that we wont step on any * i2c operations - they are all serialized here in the ISR tick. It also * insures that we get called at equal timing for good visual effect. * * The buttonlight service runs only after all backlight services have finished. * Fading the buttonlights is possible, but not recommended because of the * additional calls needed during the ISR */ static void led_control_service(void) { if(initial_tick_delay) { initial_tick_delay--; return; } switch (backlight_control) { case BACKLIGHT_CONTROL_IDLE: switch (buttonlight_state) { case BUTTONLIGHT_MODE_STOP: break; /* Buttonlight mode: OFF */ case BUTTONLIGHT_MODE_OFF_ENTRY: if (buttonlight_current) { buttonlight_leds = 0x00; sc606_write(SC606_REG_CONF, backlight_leds); buttonlight_current = 0; } buttonlight_state = BUTTONLIGHT_MODE_OFF; break; case BUTTONLIGHT_MODE_OFF: break; /* button mode: CHARGING - show charging sequence */ case BUTTONLIGHT_MODE_CHARGING_ENTRY: /* start turned off */ buttonlight_leds = 0x00; sc606_write(SC606_REG_CONF, backlight_leds); buttonlight_current = 0; /* temporary save for the next mode - then to do settings */ buttonlight_setting = DEFAULT_BRIGHTNESS_SETTING; buttonlight_saved_state = BUTTONLIGHT_MODE_CHARGING_WAIT; buttonlight_state = BUTTONLIGHT_HELPER_SET; break; case BUTTONLIGHT_MODE_CHARGING: if (--buttonlight_charging_counter == 0) { /* change led */ if (charging_state()) { buttonlight_leds = charging_leds[charging_led_index]; if (++charging_led_index >= sizeof(charging_leds)) { charging_led_index = 0; } sc606_write(SC606_REG_CONF, backlight_leds | buttonlight_leds); buttonlight_charging_counter = CHARGING_LED_COUNT; } else { buttonlight_state = BUTTONLIGHT_MODE_CHARGING_ENTRY; } } break; /* wait for the charget to be plugged in */ case BUTTONLIGHT_MODE_CHARGING_WAIT: if (charging_state()) { charging_led_index = 0; buttonlight_charging_counter = CHARGING_LED_COUNT; buttonlight_state = BUTTONLIGHT_MODE_CHARGING; } break; /* Buttonlight mode: FOLLOW - try to stay current with backlight * since this runs in the idle of the backlight it will not really * follow in real time */ case BUTTONLIGHT_MODE_FOLLOW_ENTRY: /* case 1 - backlight on, but buttonlight is off */ if (backlight_current) { /* Turn the buttonlights on */ buttonlight_leds = buttonlight_selected; sc606_write(SC606_REG_CONF, backlight_leds | buttonlight_leds); /* temporary save for the next mode - then to do settings */ buttonlight_setting = backlight_current; buttonlight_saved_state = BUTTONLIGHT_MODE_FOLLOW; buttonlight_state = BUTTONLIGHT_HELPER_SET; } /* case 2 - backlight off, but buttonlight is on */ else { buttonlight_current = 0; buttonlight_leds = 0x00; sc606_write(SC606_REG_CONF, backlight_leds); buttonlight_state = BUTTONLIGHT_MODE_FOLLOW; } break; case BUTTONLIGHT_MODE_FOLLOW: if (buttonlight_current != backlight_current) { /* case 1 - backlight on, but buttonlight is off */ if (backlight_current) { if (0 == buttonlight_current) { /* Turn the buttonlights on */ buttonlight_leds = buttonlight_selected; sc606_write(SC606_REG_CONF, backlight_leds | buttonlight_leds); } /* temporary save for the next mode - then to do settings */ buttonlight_setting = backlight_current; buttonlight_saved_state = BUTTONLIGHT_MODE_FOLLOW; buttonlight_state = BUTTONLIGHT_HELPER_SET; } /* case 2 - backlight off, but buttonlight is on */ else { buttonlight_current = 0; buttonlight_leds = 0x00; sc606_write(SC606_REG_CONF, backlight_leds); } } break; /* Buttonlight mode: ON - stays at the set brightness */ case BUTTONLIGHT_MODE_ON_ENTRY: buttonlight_leds = buttonlight_selected; sc606_write(SC606_REG_CONF, backlight_leds | buttonlight_leds); /* temporary save for the next mode - then to do settings */ buttonlight_setting = buttonlight_trigger_brightness; buttonlight_saved_state = BUTTONLIGHT_MODE_ON; buttonlight_state = BUTTONLIGHT_HELPER_SET; break; case BUTTONLIGHT_MODE_ON: break; /* Buttonlight mode: FLICKER */ case BUTTONLIGHT_MODE_FLICKER_ENTRY: /* already on? turn it off */ if (buttonlight_current) { buttonlight_leds = 0x00; sc606_write(SC606_REG_CONF, backlight_leds); buttonlight_current = 0; } /* set the brightness if not already set */ if (buttonlight_current != buttonlight_trigger_brightness) { /* temporary save for the next mode - then to do settings */ buttonlight_setting = buttonlight_trigger_brightness; buttonlight_saved_state = BUTTONLIGHT_MODE_FLICKER; buttonlight_state = BUTTONLIGHT_HELPER_SET; } else buttonlight_state = BUTTONLIGHT_MODE_FLICKER; break; case BUTTONLIGHT_MODE_FLICKER: /* wait for the foreground to trigger flickering */ if (buttonlight_trigger_now) { /* turn them on */ buttonlight_leds = buttonlight_selected; buttonlight_current = buttonlight_setting; sc606_write(SC606_REG_CONF, backlight_leds | buttonlight_leds); /* reset the trigger and go flicker the LEDs */ buttonlight_trigger_now = 0; buttonlight_flickering = FLICKER_PERIOD; buttonlight_state = BUTTONLIGHT_MODE_FLICKERING; } break; case BUTTONLIGHT_MODE_FLICKERING: /* flicker the LEDs for as long as we get triggered */ if (buttonlight_flickering) { /* turn the leds off if they are on */ if (buttonlight_current) { buttonlight_leds = 0x00; sc606_write(SC606_REG_CONF, backlight_leds); buttonlight_current = 0; } buttonlight_flickering--; } else { /* is flickering triggered again? */ if (!buttonlight_trigger_now) { /* completed a cycle - no new triggers - go back and wait */ buttonlight_state = BUTTONLIGHT_MODE_FLICKER; } else { /* reset flickering */ buttonlight_trigger_now = 0; buttonlight_flickering = FLICKER_PERIOD; /* turn buttonlights on */ buttonlight_leds = buttonlight_selected; buttonlight_current = buttonlight_setting; sc606_write(SC606_REG_CONF, backlight_leds | buttonlight_leds); } } break; /* Buttonlight mode: SIGNAL / SOLID */ case BUTTONLIGHT_MODE_SOLID_ENTRY: /* already on? turn it off */ if (buttonlight_current) { buttonlight_leds = 0x00; sc606_write(SC606_REG_CONF, backlight_leds); buttonlight_current = 0; } /* set the brightness if not already set */ /* temporary save for the next mode - then to do settings */ buttonlight_setting = buttonlight_trigger_brightness; buttonlight_saved_state = BUTTONLIGHT_MODE_SOLID; buttonlight_state = BUTTONLIGHT_HELPER_SET; break; case BUTTONLIGHT_MODE_SOLID: /* wait for the foreground to trigger */ if (buttonlight_trigger_now) { /* turn them on if not already on */ if (0 == buttonlight_current) { buttonlight_leds = buttonlight_selected; buttonlight_current = buttonlight_setting; sc606_write(SC606_REG_CONF, backlight_leds | buttonlight_leds); } /* reset the trigger */ buttonlight_trigger_now = 0; } else { if (buttonlight_current) { buttonlight_leds = 0x00; sc606_write(SC606_REG_CONF, backlight_leds); buttonlight_current = 0; } } break; /* set the brightness for the buttonlights - takes 2 passes */ case BUTTONLIGHT_HELPER_SET: sc606_write(SC606_REG_B, buttonlight_setting-1); buttonlight_state = BUTTONLIGHT_HELPER_SET_FINAL; break; case BUTTONLIGHT_HELPER_SET_FINAL: sc606_write(SC606_REG_C, buttonlight_setting-1); buttonlight_current = buttonlight_setting; buttonlight_state = buttonlight_saved_state; break; default: break; } break; case BACKLIGHT_CONTROL_FADE_ON_FROM_OFF: backlight_leds = 0x03; sc606_write(SC606_REG_CONF, 0x03 | buttonlight_leds); backlight_control = BACKLIGHT_CONTROL_FADE_ON; break; case BACKLIGHT_CONTROL_OFF: backlight_current = 0; backlight_leds = 0x00; sc606_write(SC606_REG_CONF, buttonlight_leds); backlight_control = BACKLIGHT_CONTROL_IDLE; /* turn the lcd completely off after the fade or off command */ lcd_enable(false); break; case BACKLIGHT_CONTROL_ON: backlight_leds = 0x03; sc606_write(SC606_REG_CONF, 0x03 | buttonlight_leds); backlight_current = backlight_brightness; backlight_control = BACKLIGHT_CONTROL_IDLE; break; case BACKLIGHT_CONTROL_SET: /* The SC606 LED driver can set the brightness in 64 steps */ sc606_write(SC606_REG_A, backlight_brightness-1); /* if we were turned off - turn the backlight on */ if (backlight_current) { backlight_current = backlight_brightness; backlight_control = BACKLIGHT_CONTROL_IDLE; } else { backlight_control = BACKLIGHT_CONTROL_ON; } break; case BACKLIGHT_CONTROL_FADE_ON: if (--time_til_fade) return; /* The SC606 LED driver can set the brightness in 64 steps */ sc606_write(SC606_REG_A, backlight_current++); /* have we hit the target? */ if (backlight_current == backlight_target) { backlight_control = BACKLIGHT_CONTROL_IDLE; } else { time_til_fade = fade_interval; } break; case BACKLIGHT_CONTROL_FADE_OFF: if (--time_til_fade) return; /* The SC606 LED driver can set the brightness in 64 steps */ sc606_write(SC606_REG_A, --backlight_current); /* have we hit the target? */ if (backlight_current == backlight_target) { if (backlight_current) { backlight_control = BACKLIGHT_CONTROL_IDLE; } else { backlight_control = BACKLIGHT_CONTROL_OFF; } } else { time_til_fade = fade_interval; } break; } if(backlight_current) lcd_enable(true); else lcd_enable(false); } void __backlight_dim(bool dim_now) { unsigned short target; /* dont let the interrupt tick happen */ backlight_control = BACKLIGHT_CONTROL_IDLE; target = (dim_now == true) ? 0 : backlight_brightness; /* only try and fade if the target is different */ if (backlight_current != target) { backlight_target = target; if (backlight_current > backlight_target) { time_til_fade = fade_interval = 4; backlight_control = BACKLIGHT_CONTROL_FADE_OFF; } else { time_til_fade = fade_interval = 1; if (backlight_current) { backlight_control = BACKLIGHT_CONTROL_FADE_ON; } else { backlight_control = BACKLIGHT_CONTROL_FADE_ON_FROM_OFF; } } } }