rockbox/firmware/target/arm/gigabeat/meg-fx/backlight-meg-fx.c

545 lines
15 KiB
C

/***************************************************************************
* __________ __ ___.
* 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"
#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 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 same brightness as backlight */
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 glow */
BUTTONLIGHT_MODE_GLOW_ENTRY,
BUTTONLIGHT_MODE_GLOW,
/* internal use only */
BUTTONLIGHT_HELPER_SET,
BUTTONLIGHT_HELPER_SET_FINAL,
};
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_flicker_now;
static unsigned short buttonlight_flicker_brightness;
void __backlight_init(void)
{
backlight_control = BACKLIGHT_CONTROL_IDLE;
backlight_current = DEFAULT_BRIGHTNESS_SETTING;
buttonlight_state = BUTTONLIGHT_MODE_OFF;
buttonlight_selected = 0x04;
/* put the led control on the tick list */
tick_add_task(led_control_service);
}
void __backlight_on(void)
{
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 flicker */
void __buttonlight_flicker(unsigned short brightness)
{
/* clip the setting */
if (brightness > MAX_BRIGHTNESS_SETTING)
{
brightness = MAX_BRIGHTNESS_SETTING;
}
/* add one because we subtract later for full range */
buttonlight_flicker_brightness = brightness + 1;
buttonlight_flicker_now = 1;
}
/* select which LEDs light up
* The only pleasing combinations are: only menu/power or all LEDs
*/
void __buttonlight_select(enum buttonlight_selection selection)
{
switch (selection)
{
default:
case BUTTONLIGHT_LED_MENU:
buttonlight_selected = BUTTONLIGHT_MENU;
break;
case BUTTONLIGHT_LED_ALL:
buttonlight_selected = BUTTONLIGHT_ALL;
break;
}
}
/* map the mode from the command into the state machine entries */
void __buttonlight_mode(enum buttonlight_mode mode)
{
switch (mode)
{
case BUTTONLIGHT_OFF:
buttonlight_state = BUTTONLIGHT_MODE_OFF_ENTRY;
break;
case BUTTONLIGHT_ON:
buttonlight_state = BUTTONLIGHT_MODE_ON_ENTRY;
break;
case BUTTONLIGHT_FAINT:
buttonlight_state = BUTTONLIGHT_MODE_FAINT_ENTRY;
break;
case BUTTONLIGHT_FLICKER:
buttonlight_state = BUTTONLIGHT_MODE_FLICKER_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)
{
switch (backlight_control)
{
case BACKLIGHT_CONTROL_IDLE:
switch (buttonlight_state)
{
/* 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;
/* Buttonlight mode: ON */
case BUTTONLIGHT_MODE_ON_ENTRY:
case BUTTONLIGHT_MODE_ON:
if (buttonlight_current != backlight_brightness ||
(buttonlight_leds != buttonlight_selected))
{
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_brightness;
buttonlight_saved_state = BUTTONLIGHT_MODE_ON;
buttonlight_state = BUTTONLIGHT_HELPER_SET;
}
break;
/* Buttonlight mode: Faint */
case BUTTONLIGHT_MODE_FAINT_ENTRY:
if (buttonlight_current != 1)
{
/* need to turn on the backlight? */
if (buttonlight_current == 0)
{
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 = 1;
buttonlight_saved_state = BUTTONLIGHT_MODE_FAINT;
buttonlight_state = BUTTONLIGHT_HELPER_SET;
}
break;
case BUTTONLIGHT_MODE_FAINT:
/* watch for change in led selction */
if (buttonlight_leds != buttonlight_selected)
{
buttonlight_leds = buttonlight_selected;
sc606_write(SC606_REG_CONF, backlight_leds | buttonlight_leds);
}
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_flicker_brightness)
{
/* temporary save for the next mode - then to do settings */
buttonlight_setting = buttonlight_flicker_brightness;
buttonlight_saved_state = BUTTONLIGHT_MODE_FLICKER;
buttonlight_state = BUTTONLIGHT_HELPER_SET;
}
break;
case BUTTONLIGHT_MODE_FLICKER:
/* wait for the foreground to trigger flickering */
if (buttonlight_flicker_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_flicker_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_flicker_now)
{
/* completed a cycle - no new triggers - go back and wait */
buttonlight_state = BUTTONLIGHT_MODE_FLICKER;
}
else
{
/* reset flickering */
buttonlight_flicker_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;
/* 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;
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;
}
}
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;
}
}
}
}
#define BACKLIGHT_BUTTONS_OFF 0
#define BACKLIGHT_BUTTONS_ON 1
#define BACKLIGHT_BUTTONS_FAINT 2
#define BACKLIGHT_BUTTONS_FOLLOW 3
void __backlight_buttons(int value)
{
switch (value)
{
default:
case BACKLIGHT_BUTTONS_OFF:
break;
case BACKLIGHT_BUTTONS_ON:
break;
case BACKLIGHT_BUTTONS_FAINT:
break;
case BACKLIGHT_BUTTONS_FOLLOW:
break;
}
}