summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/lightbar.c765
1 files changed, 388 insertions, 377 deletions
diff --git a/common/lightbar.c b/common/lightbar.c
index 6a1a12c820..0f448c4cba 100644
--- a/common/lightbar.c
+++ b/common/lightbar.c
@@ -66,7 +66,7 @@ static inline uint8_t controller_read(int ctrl_num, uint8_t reg)
#define MAX_GREEN 0x30
#define MAX_BLUE 0x67
-/* How many LEDs do we have? */
+/* How many (logical) LEDs do we have? */
#define NUM_LEDS 4
/* How we'd like to see the driver chips initialized. The controllers have some
@@ -104,17 +104,18 @@ static void set_from_array(const struct initdata_s *data, int count)
}
/* Controller register lookup tables. */
-static const uint8_t led_to_ctrl[] = { 0, 0, 1, 1 };
-static const uint8_t led_to_isc[] = { 0x15, 0x18, 0x15, 0x18 };
+static const uint8_t led_to_ctrl[] = { 1, 1, 0, 0 };
+static const uint8_t led_to_isc[] = { 0x18, 0x15, 0x18, 0x15 };
/* Scale 0-255 into max value */
static inline uint8_t scale_abs(int val, int max)
{
- return (val * max)/255 + max/256;
+ return (val * max)/255;
}
/* It will often be simpler to provide an overall brightness control. */
-static int brightness = 0x80;
+static int brightness = 0xff;
+
/* So that we can make brightness changes happen instantly, we need to track
* the current values. The values in the controllers aren't very helpful. */
@@ -133,6 +134,10 @@ static void lightbar_init_vals(void)
memset(current, 0, sizeof(current));
}
+/* Change it with this function (defined below). */
+static void lightbar_brightness(int newval);
+
+
/* Helper function. */
static void setrgb(int led, int red, int green, int blue)
{
@@ -149,72 +154,9 @@ static void setrgb(int led, int red, int green, int blue)
/******************************************************************************/
-/* Basic LED control functions. */
-/******************************************************************************/
-
-static void lightbar_off(void)
-{
- CPRINTF("[%T LB_off]\n");
- /* Just go into standby mode. No register values should change. */
- controller_write(0, 0x01, 0x00);
- controller_write(1, 0x01, 0x00);
-}
-
-static void lightbar_on(void)
-{
- CPRINTF("[%T LB_on]\n");
- /* Come out of standby mode. */
- controller_write(0, 0x01, 0x20);
- controller_write(1, 0x01, 0x20);
-}
-
-
-/* LEDs are numbered 0-3, RGB values should be in 0-255.
- * If you specify too large an LED, it sets them all. */
-static void lightbar_setrgb(int led, int red, int green, int blue)
-{
- int i;
- if (led >= NUM_LEDS)
- for (i = 0; i < NUM_LEDS; i++)
- setrgb(i, red, green, blue);
- else
- setrgb(led, red, green, blue);
-}
-
-static void lightbar_brightness(int newval)
-{
- int i;
- CPRINTF("[%T LB_bright 0x%02x]\n", newval);
- brightness = newval;
- for (i = 0; i < NUM_LEDS; i++)
- lightbar_setrgb(i, current[i][0],
- current[i][1], current[i][2]);
-}
-
-
-/******************************************************************************/
-
-/* Major colors */
-static const struct {
- uint8_t r, g, b;
-} testy[] = {
- {0xff, 0x00, 0x00},
- {0x00, 0xff, 0x00},
- {0x00, 0x00, 0xff},
- {0xff, 0xff, 0x00}, /* The first four are Google colors */
- {0x00, 0xff, 0xff},
- {0xff, 0x00, 0xff},
- {0xff, 0xff, 0xff},
-};
-
-
-/******************************************************************************/
-/* Now for the pretty patterns */
-/******************************************************************************/
-
/* Here's some state that we might want to maintain across sysjumps, just to
* prevent the lightbar from flashing during normal boot as the EC jumps from
- * RO to RW. FIXME: This doesn't quite stop the problems. */
+ * RO to RW. */
static struct {
/* What patterns are we showing? */
enum lightbar_sequence cur_seq;
@@ -223,8 +165,13 @@ static struct {
/* Quantized battery charge level: 0=low 1=med 2=high 3=full. */
int battery_level;
- /* We'll pulse slightly faster when charging */
+ /* It's either charging or discharging. */
int battery_is_charging;
+
+ /* Pattern variables for state S0. */
+ uint8_t w0; /* primary phase */
+ uint8_t amp; /* amplitude */
+ uint8_t ramp; /* ramp-in for S3->S0 */
} st;
#define LB_SYSJUMP_TAG 0x4c42 /* "LB" */
@@ -246,52 +193,77 @@ static void lb_restore_state(void)
} else {
st.cur_seq = st.prev_seq = LIGHTBAR_S5;
st.battery_level = 3;
+ st.w0 = 0;
+ st.amp = 0;
+ st.ramp = 0;
}
CPRINTF("[%T LB state: %d %d - %d/%d]\n",
st.cur_seq, st.prev_seq,
st.battery_is_charging, st.battery_level);
}
-/* Here's where we keep messages waiting to be delivered to lightbar task. If
- * more than one is sent before the task responds, we only want to deliver the
- * latest one. */
-static uint32_t pending_msg;
-/* And here's the task event that we use to trigger delivery. */
-#define PENDING_MSG 1
+/******************************************************************************/
+/* The patterns are generally dependent on the current battery level and AC
+ * state. These functions obtain that information, generally by querying the
+ * power manager task. In demo mode, the keyboard task forces changes to the
+ * state by calling the demo_* functions directly. */
+/******************************************************************************/
-/* Interruptible delay */
-#define WAIT_OR_RET(A) do { \
- uint32_t msg = task_wait_event(A); \
- if (TASK_EVENT_CUSTOM(msg) == PENDING_MSG) \
- return PENDING_MSG; } while (0)
+#ifdef CONFIG_TASK_PWM
+static int last_backlight_level;
+#endif
-/****************************************************************************/
-/* Demo sequence */
+static int demo_mode;
-struct rgb_s {
- uint8_t r, g, b;
-};
-enum {
- COLOR_LOW, COLOR_MEDIUM, COLOR_HIGH, COLOR_FULL, COLOR_BLACK,
-};
-static const struct rgb_s colors[] = {
- {0xff, 0x00, 0x00}, /* low = red */
- {0xff, 0xff, 0x00}, /* med = yellow */
- {0x00, 0xff, 0x00}, /* high = green */
- {0x00, 0x00, 0xff}, /* full = blue */
- {0x00, 0x00, 0x00}, /* black */
-};
+/* Update the known state. */
+static void get_battery_level(void)
+{
+ int pct = 0;
-static int demo_mode;
+ if (demo_mode)
+ return;
+
+#ifdef CONFIG_TASK_PWM
+ /* With nothing else to go on, use the keyboard backlight level to
+ * set the brightness. If the keyboard backlight is OFF (which it is
+ * when ambient is bright), use max brightness for lightbar. If
+ * keyboard backlight is ON, use keyboard backlight brightness.
+ */
+ if (pwm_get_keyboard_backlight_enabled()) {
+ pct = pwm_get_keyboard_backlight();
+ if (pct != last_backlight_level) {
+ last_backlight_level = pct;
+ pct = (255 * pct) / 100;
+ lightbar_brightness(pct);
+ }
+ } else
+ lightbar_brightness(255);
+#endif
+
+#ifdef CONFIG_TASK_POWERSTATE
+ pct = charge_get_percent();
+ st.battery_is_charging = (PWR_STATE_DISCHARGE != charge_get_state());
+#endif
+
+ /* We're only using two of the four levels at the moment. */
+ if (pct > LIGHTBAR_POWER_THRESHOLD_MEDIUM)
+ st.battery_level = 3;
+ else
+ st.battery_level = 0;
+}
+
+
+/* Forcing functions for demo mode */
void demo_battery_level(int inc)
{
- /* Only using two colors */
if (!demo_mode)
return;
- if (inc > 0)
+
+ st.battery_level += inc;
+ if (st.battery_level > 3)
st.battery_level = 3;
- else
+ else if (st.battery_level < 0)
st.battery_level = 0;
CPRINTF("[%T LB demo: battery_level=%d]\n", st.battery_level);
@@ -301,6 +273,7 @@ void demo_is_charging(int ischarge)
{
if (!demo_mode)
return;
+
st.battery_is_charging = ischarge;
CPRINTF("[%T LB demo: battery_is_charging=%d]\n",
st.battery_is_charging);
@@ -321,189 +294,237 @@ void demo_brightness(int inc)
lightbar_brightness(b);
}
-static int last_battery_is_charging;
-static int last_battery_level;
-#ifdef CONFIG_TASK_PWM
-static int last_backlight_level;
-#endif
-
-static void get_battery_level(void)
-{
- int pct = 0;
- if (demo_mode)
- return;
+/******************************************************************************/
+/* Basic LED control functions. Use these to implement the pretty patterns. */
+/******************************************************************************/
-#ifdef CONFIG_TASK_PWM
- /* With nothing else to go on, use the keyboard backlight level to
- * set the brightness. If the keyboard backlight is OFF (which it is
- * when ambient is bright), use max brightness for lightbar. If
- * keyboard backlight is ON, use keyboard backlight brightness.
- */
- if (pwm_get_keyboard_backlight_enabled()) {
- pct = pwm_get_keyboard_backlight();
- if (pct != last_backlight_level) {
- last_backlight_level = pct;
- pct = (255 * pct) / 100;
- lightbar_brightness(pct);
- }
- } else
- lightbar_brightness(255);
-#endif
+/* Just go into standby mode. No register values should change. */
+static void lightbar_off(void)
+{
+ CPRINTF("[%T LB_off]\n");
+ controller_write(0, 0x01, 0x00);
+ controller_write(1, 0x01, 0x00);
+}
-#ifdef CONFIG_TASK_POWERSTATE
- pct = charge_get_percent();
- st.battery_is_charging = (PWR_STATE_DISCHARGE != charge_get_state());
-#endif
+/* Come out of standby mode. */
+static void lightbar_on(void)
+{
+ CPRINTF("[%T LB_on]\n");
+ controller_write(0, 0x01, 0x20);
+ controller_write(1, 0x01, 0x20);
+}
- /* We're only using two of the four levels at the moment. */
- if (pct > LIGHTBAR_POWER_THRESHOLD_MEDIUM)
- st.battery_level = COLOR_FULL;
+/* LEDs are numbered 0-3, RGB values should be in 0-255.
+ * If you specify too large an LED, it sets them all. */
+static void lightbar_setrgb(int led, int red, int green, int blue)
+{
+ int i;
+ if (led >= NUM_LEDS)
+ for (i = 0; i < NUM_LEDS; i++)
+ setrgb(i, red, green, blue);
else
- st.battery_level = COLOR_LOW;
+ setrgb(led, red, green, blue);
}
-static struct {
- timestamp_t start_time;
- timestamp_t end_time;
- struct rgb_s prev;
- struct rgb_s next;
-} led_state[NUM_LEDS];
-
-#define MSECS(a) (a * 1000)
-#define SEC(a) (a * 1000000)
-
-static const uint64_t transition_time = SEC(3);
-static const uint64_t transition_stagger[NUM_LEDS] = {
- MSECS(0), MSECS(200), MSECS(733), MSECS(450),
+/* Change current display brightness (0-255) */
+static void lightbar_brightness(int newval)
+{
+ int i;
+ CPRINTF("[%T LB_bright 0x%02x]\n", newval);
+ brightness = newval;
+ for (i = 0; i < NUM_LEDS; i++)
+ lightbar_setrgb(i, current[i][0],
+ current[i][1], current[i][2]);
+}
+
+/******************************************************************************/
+/* Helper functions and data. */
+/******************************************************************************/
+
+struct rgb_s {
+ uint8_t r, g, b;
};
-static const int pulse_period[2] = { SEC(20), /* discharging */
- SEC(10) }; /* charging */
+/* These are the official Google colors, in order. */
+enum { BLUE = 0, RED, YELLOW, GREEN, INVALID };
+static const struct rgb_s google[] = {
+ {0x33, 0x69, 0xe8}, /* blue */
+ {0xd5, 0x0f, 0x25}, /* red */
+ {0xee, 0xb2, 0x11}, /* yellow */
+ {0x00, 0x99, 0x25}, /* green */
-static const int pulse_stagger[2][NUM_LEDS] = {
- { MSECS(0), MSECS(4800), MSECS(16000), MSECS(11000) }, /* discharging */
- { MSECS(0), MSECS(2400), MSECS(8000), MSECS(5500) } /* charging */
+ {0xff, 0x00, 0xFF}, /* invalid */
};
-static struct rgb_s tmp_color;
-static int tmp_percent;
-static void interpolate(timestamp_t now, int i)
-{
- int range, sofar;
- if (now.val <= led_state[i].start_time.val) {
- tmp_color = led_state[i].prev;
- tmp_percent = 0;
- return;
- }
+/* These are used for test patterns. */
+static const struct rgb_s colors[] = {
+ {0xff, 0x00, 0x00},
+ {0xff, 0xff, 0x00},
+ {0x00, 0xff, 0x00},
+ {0x00, 0x00, 0xff},
+ {0x00, 0xff, 0xff},
+ {0xff, 0x00, 0xff},
+ {0x00, 0x00, 0x00},
+};
- if (now.val >= led_state[i].end_time.val) {
- tmp_percent = 100;
- tmp_color = led_state[i].next;
- return;
- }
+/* Map battery_level to one of the google colors */
+static const int battery_color[] = { RED, YELLOW, GREEN, BLUE };
+
+const float _ramp_table[] = {
+ 0.000000f, 0.000151f, 0.000602f, 0.001355f, 0.002408f, 0.003760f,
+ 0.005412f, 0.007361f, 0.009607f, 0.012149f, 0.014984f, 0.018112f,
+ 0.021530f, 0.025236f, 0.029228f, 0.033504f, 0.038060f, 0.042895f,
+ 0.048005f, 0.053388f, 0.059039f, 0.064957f, 0.071136f, 0.077573f,
+ 0.084265f, 0.091208f, 0.098396f, 0.105827f, 0.113495f, 0.121396f,
+ 0.129524f, 0.137876f, 0.146447f, 0.155230f, 0.164221f, 0.173414f,
+ 0.182803f, 0.192384f, 0.202150f, 0.212096f, 0.222215f, 0.232501f,
+ 0.242949f, 0.253551f, 0.264302f, 0.275194f, 0.286222f, 0.297379f,
+ 0.308658f, 0.320052f, 0.331555f, 0.343159f, 0.354858f, 0.366644f,
+ 0.378510f, 0.390449f, 0.402455f, 0.414519f, 0.426635f, 0.438795f,
+ 0.450991f, 0.463218f, 0.475466f, 0.487729f, 0.500000f, 0.512271f,
+ 0.524534f, 0.536782f, 0.549009f, 0.561205f, 0.573365f, 0.585481f,
+ 0.597545f, 0.609551f, 0.621490f, 0.633356f, 0.645142f, 0.656841f,
+ 0.668445f, 0.679947f, 0.691342f, 0.702621f, 0.713778f, 0.724806f,
+ 0.735698f, 0.746449f, 0.757051f, 0.767499f, 0.777785f, 0.787904f,
+ 0.797850f, 0.807616f, 0.817197f, 0.826586f, 0.835780f, 0.844770f,
+ 0.853553f, 0.862124f, 0.870476f, 0.878604f, 0.886505f, 0.894173f,
+ 0.901604f, 0.908792f, 0.915735f, 0.922427f, 0.928864f, 0.935044f,
+ 0.940961f, 0.946612f, 0.951995f, 0.957105f, 0.961940f, 0.966496f,
+ 0.970772f, 0.974764f, 0.978470f, 0.981888f, 0.985016f, 0.987851f,
+ 0.990393f, 0.992639f, 0.994588f, 0.996240f, 0.997592f, 0.998645f,
+ 0.999398f, 0.999849f, 1.000000f,
+};
- range = (int)(led_state[i].end_time.val - led_state[i].start_time.val);
- sofar = (int)(now.val - led_state[i].start_time.val);
+/* This function provides a smooth ramp up from 0.0 to 1.0 and back to 0.0,
+ * for input from 0x00 to 0xff. */
+static inline float cycle_010(uint8_t i)
+{
+ return i < 128 ? _ramp_table[i] : _ramp_table[256-i];
+}
- tmp_percent = (sofar * 100) / range;
- tmp_color.r = ((100 - tmp_percent) * led_state[i].prev.r) / 100 +
- (tmp_percent * led_state[i].next.r) / 100;
- tmp_color.g = ((100 - tmp_percent) * led_state[i].prev.g) / 100 +
- (tmp_percent * led_state[i].next.g) / 100;
- tmp_color.b = ((100 - tmp_percent) * led_state[i].prev.b) / 100 +
- (tmp_percent * led_state[i].next.b) / 100;
+/* This function provides a smooth oscillation between -0.5 and +0.5.
+ * Zero starts at 0x00. */
+static inline float cycle_0P0N0(uint8_t i)
+{
+ return cycle_010(i+64) - 0.5f;
}
+/******************************************************************************/
+/* Here's where we keep messages waiting to be delivered to the lightbar task.
+ * If more than one is sent before the task responds, we only want to deliver
+ * the latest one. */
+static uint32_t pending_msg;
+/* And here's the task event that we use to trigger delivery. */
+#define PENDING_MSG 1
+
+/* Interruptible delay. */
+#define WAIT_OR_RET(A) do { \
+ uint32_t msg = task_wait_event(A); \
+ if (TASK_EVENT_CUSTOM(msg) == PENDING_MSG) \
+ return PENDING_MSG; } while (0)
+
+/* Handy conversions */
+#define MSECS(a) ((a) * 1000)
+#define SEC(a) ((a) * 1000000)
-/* 8-bit fixed-point sin(x). domain 0-PI == 0-127, range 0-1 == 0-255.
- * This is just the first half cycle. */
-const uint8_t sin_table[] = {
- 0, 6, 13, 19, 25, 31, 37, 44, 50, 56, 62, 68, 74, 80, 86, 92, 98,
- 103, 109, 115, 120, 126, 131, 136, 142, 147, 152, 157, 162, 167,
- 171, 176, 180, 185, 189, 193, 197, 201, 205, 208, 212, 215, 219,
- 222, 225, 228, 231, 233, 236, 238, 240, 242, 244, 246, 247, 249,
- 250, 251, 252, 253, 254, 254, 255, 255, 255, 255, 255, 254, 254,
- 253, 252, 251, 250, 249, 247, 246, 244, 242, 240, 238, 236, 233,
- 231, 228, 225, 222, 219, 215, 212, 208, 205, 201, 197, 193, 189,
- 185, 180, 176, 171, 167, 162, 157, 152, 147, 142, 136, 131, 126,
- 120, 115, 109, 103, 98, 92, 86, 80, 74, 68, 62, 56, 50, 44, 37, 31,
- 25, 19, 13, 6
-};
-/* This provides the other half. */
-int sini(uint8_t i)
+/******************************************************************************/
+/* Here are the preprogrammed sequences. */
+/******************************************************************************/
+
+/* Pulse google colors once, off to on to off. */
+static uint32_t pulse_google_colors(void)
{
- if (i < 128)
- return sin_table[i];
- return -sin_table[i-128];
+ int w, i, r, g, b;
+ float f;
+
+ for (w = 0; w < 128; w += 2) {
+ f = cycle_010(w);
+ for (i = 0; i < NUM_LEDS; i++) {
+ r = google[i].r * f;
+ g = google[i].g * f;
+ b = google[i].b * f;
+ lightbar_setrgb(i, r, g, b);
+ }
+ WAIT_OR_RET(2500);
+ }
+ for (w = 128; w <= 256; w++) {
+ f = cycle_010(w);
+ for (i = 0; i < NUM_LEDS; i++) {
+ r = google[i].r * f;
+ g = google[i].g * f;
+ b = google[i].b * f;
+ lightbar_setrgb(i, r, g, b);
+ }
+ WAIT_OR_RET(10000);
+ }
+
+ return 0;
}
-static void pulse(timestamp_t now, int period_offset)
+/* Constants */
+#define MIN_S0 0.25f
+#define MAX_S0 1.0f
+#define BASE_S0 ((MIN_S0 + MAX_S0) * 0.5f)
+#define OSC_S0 (MAX_S0 - MIN_S0)
+
+/* CPU is waking from sleep. */
+static uint32_t sequence_S3S0(void)
{
- int t;
- uint8_t i;
- int j;
-
- /* Bound time to one cycle */
- t = (now.le.lo + period_offset) % pulse_period[st.battery_is_charging];
- /* Convert phase to 0-255 */
- i = ((t >> 8) / (pulse_period[st.battery_is_charging] >> 16));
- /* Compute sinusoidal for phase, as [-255:255] */
- j = sini(i);
- j = j * sini((int)i * 3 / 2) / 255;
- j = j * sini((int)i * 16 / 10) / 255;
- /* Cut it down a bit if we're plugged in. */
- j = j / (1 + st.battery_is_charging);
-
- /* Luminize current color using sinusoidal */
- t = j + tmp_color.r;
- if (t > 255)
- tmp_color.r = 255;
- else if (t < 0)
- tmp_color.r = 0;
- else
- tmp_color.r = t;
+ int w, r, g, b;
+ float f;
+ int ci;
+ uint32_t res;
- t = j + tmp_color.g;
- if (t > 255)
- tmp_color.g = 255;
- else if (t < 0)
- tmp_color.g = 0;
- else
- tmp_color.g = t;
+ lightbar_init_vals();
+ lightbar_on();
- t = j + tmp_color.b;
- if (t > 255)
- tmp_color.b = 255;
- else if (t < 0)
- tmp_color.b = 0;
- else
- tmp_color.b = t;
-}
+ res = pulse_google_colors();
+ if (res)
+ return res;
+
+ /* Ramp up to base brightness. */
+ get_battery_level();
+ ci = battery_color[st.battery_level];
+ for (w = 0; w <= 128; w++) {
+ f = cycle_010(w) * BASE_S0;
+ r = google[ci].r * f;
+ g = google[ci].g * f;
+ b = google[ci].b * f;
+ lightbar_setrgb(NUM_LEDS, r, g, b);
+ WAIT_OR_RET(2000);
+ }
+ /* Initial conditions */
+ st.w0 = 0;
+ st.amp = 0;
+ st.ramp = 0;
+
+ /* Ready for S0 */
+ return 0;
+}
/* CPU is fully on */
static uint32_t sequence_S0(void)
{
- int i, tick, last_tick;
+ int tick, last_tick;
timestamp_t start, now;
+ uint32_t r, g, b;
+ int i, ci;
+ uint8_t w, target_amp;
+ float f, ff;
start = get_time();
tick = last_tick = 0;
- lightbar_on();
-
- /* start black, we'll fade in first thing */
lightbar_setrgb(NUM_LEDS, 0, 0, 0);
- for (i = 0; i < NUM_LEDS; i++)
- led_state[i].prev = colors[COLOR_BLACK];
- last_battery_is_charging = !st.battery_is_charging; /* force update */
+ lightbar_on();
while (1) {
now = get_time();
- /* Only check the battery state every so often. The battery
+ /* Only check the battery state every few seconds. The battery
* charging task doesn't update as quickly as we do, and isn't
* always valid for a bit after jumping from RO->RW. */
tick = (now.le.lo - start.le.lo) / SEC(1);
@@ -512,89 +533,74 @@ static uint32_t sequence_S0(void)
last_tick = tick;
}
- /* Has something changed? */
- if (st.battery_is_charging != last_battery_is_charging ||
- st.battery_level != last_battery_level) {
- /* yes */
- for (i = 0; i < NUM_LEDS; i++) {
- led_state[i].start_time.val = now.val +
- transition_stagger[i];
- led_state[i].end_time.val =
- led_state[i]. start_time.val +
- transition_time;
- led_state[i].prev = led_state[i].next;
- led_state[i].next = colors[st.battery_level];
- }
- last_battery_is_charging = st.battery_is_charging;
- last_battery_level = st.battery_level;
- }
-
- /* Figure out what colors to show now */
+ /* Calculate the colors */
+ ci = battery_color[st.battery_level];
+ ff = st.amp / 255.0f;
for (i = 0; i < NUM_LEDS; i++) {
- /* Compute transition between prev and next colors. */
- interpolate(now, i);
-
- /* Pulse sinusoidally */
- pulse(now, pulse_stagger[st.battery_is_charging][i]);
+ w = st.w0 - i * 24 * st.ramp / 255;
+ f = BASE_S0 + OSC_S0 * cycle_0P0N0(w) * ff;
+ r = google[ci].r * f;
+ g = google[ci].g * f;
+ b = google[ci].b * f;
+ lightbar_setrgb(i, r, g, b);
+ }
- /* Show it */
- lightbar_setrgb(i, tmp_color.r, tmp_color.g,
- tmp_color.b);
+ /* Move gradually towards the target amplitude */
+ target_amp = st.battery_is_charging ? 0xff : 0x80;
+ if (st.amp > target_amp)
+ st.amp--;
+ else if (st.amp < target_amp)
+ st.amp++;
+
+ /* Increment the phase */
+ if (st.battery_is_charging) {
+ st.w0--;
+ WAIT_OR_RET(MSECS(2 * 15));
+ } else {
+ st.w0++;
+ WAIT_OR_RET(MSECS(3 * 15));
}
- WAIT_OR_RET(MSECS(15));
+ /* Continue ramping in if needed */
+ if (st.ramp < 0xff)
+ st.ramp++ ;
}
return 0;
}
-/* CPU is off */
-static uint32_t sequence_S5(void)
-{
- /* Just wait forever. */
- lightbar_off();
- WAIT_OR_RET(-1);
- return 0;
-}
-
-/* CPU is powering up. The lightbar loses power when the CPU is in S5, so this
- * might not be useful. */
-static uint32_t sequence_S5S3(void)
-{
- /* The controllers need 100us after power is applied before they'll
- * respond. Don't return early, because we still want to initialize the
- * lightbar even if another message comes along while we're waiting. */
- usleep(100);
- lightbar_init_vals();
-
- /* For now, do something to indicate this transition.
- * We might see it. */
- lightbar_on();
- lightbar_setrgb(NUM_LEDS, 0, 0, 0);
- WAIT_OR_RET(500000);
- return 0;
-}
-
-/* CPU is going to sleep */
+/* CPU is going to sleep. */
static uint32_t sequence_S0S3(void)
{
- int i;
- for (i = 0; i < NUM_LEDS; i++)
- lightbar_setrgb(i, testy[i].r, testy[i].g, testy[i].b);
+ int w, i, r, g, b;
+ float f;
+ uint8_t drop[NUM_LEDS][3];
+
+ /* Grab current colors */
+ memcpy(drop, current, sizeof(drop));
- WAIT_OR_RET(200000);
- for (i = 0; i < NUM_LEDS; i++) {
- lightbar_setrgb(i, 0, 0, 0);
- WAIT_OR_RET(200000);
+ /* Fade down to black */
+ for (w = 128; w <= 256; w++) {
+ f = cycle_010(w);
+ for (i = 0; i < NUM_LEDS; i++) {
+ r = drop[i][0] * f;
+ g = drop[i][1] * f;
+ b = drop[i][2] * f;
+ lightbar_setrgb(i, r, g, b);
+ }
+ WAIT_OR_RET(2000);
}
- return 0;
+ /* pulse once and done */
+ return pulse_google_colors();
}
/* CPU is sleeping */
static uint32_t sequence_S3(void)
{
int r, g, b;
- int i;
+ int w;
+ float f;
+ int ci;
lightbar_off();
lightbar_init_vals();
@@ -602,22 +608,20 @@ static uint32_t sequence_S3(void)
while (1) {
WAIT_OR_RET(SEC(15));
get_battery_level();
+
+ /* only pulse if we're off AC and the battery level is low */
+ if (st.battery_is_charging || st.battery_level > 0)
+ continue;
+
+ /* pulse once */
+ ci = battery_color[st.battery_level];
lightbar_on();
- r = colors[st.battery_level].r;
- g = colors[st.battery_level].g;
- b = colors[st.battery_level].b;
- for (i = 0; i < 255; i += 5) {
- lightbar_setrgb(NUM_LEDS,
- (r * i) / 255,
- (g * i) / 255,
- (b * i) / 255);
- WAIT_OR_RET(15000);
- }
- for (i = 255; i > 0; i -= 5) {
- lightbar_setrgb(NUM_LEDS,
- (r * i) / 255,
- (g * i) / 255,
- (b * i) / 255);
+ for (w = 0; w < 255; w += 5) {
+ f = cycle_010(w);
+ r = google[ci].r * f;
+ g = google[ci].g * f;
+ b = google[ci].b * f;
+ lightbar_setrgb(NUM_LEDS, r, g, b);
WAIT_OR_RET(15000);
}
lightbar_setrgb(NUM_LEDS, 0, 0, 0);
@@ -626,67 +630,64 @@ static uint32_t sequence_S3(void)
return 0;
}
-/* CPU is waking from sleep */
-static uint32_t sequence_S3S0(void)
+
+/* CPU is powering up. We generally boot fast enough that we don't have time
+ * to do anything interesting in the S3 state, but go straight on to S0. */
+static uint32_t sequence_S5S3(void)
{
- int i;
+ /* The controllers need 100us after power is applied before they'll
+ * respond. Don't return early, because we still want to initialize the
+ * lightbar even if another message comes along while we're waiting. */
+ usleep(100);
lightbar_init_vals();
+ lightbar_setrgb(NUM_LEDS, 0, 0, 0);
lightbar_on();
- for (i = 0; i < NUM_LEDS; i++) {
- lightbar_setrgb(i, testy[i].r, testy[i].g, testy[i].b);
- WAIT_OR_RET(200000);
- }
return 0;
}
-/* Sleep to off. */
+/* Sleep to off. The S3->S5 transition takes about 10msec, so just wait. */
static uint32_t sequence_S3S5(void)
{
- int i;
- /* Go ahead and do something to indicate this transition.
- * We might see it. */
- for (i = 0; i < NUM_LEDS; i++)
- lightbar_setrgb(i, testy[i].r, testy[i].g, testy[i].b);
-
- WAIT_OR_RET(200000);
- for (i = 0; i < NUM_LEDS; i++) {
- lightbar_setrgb(i, 0, 0, 0);
- WAIT_OR_RET(200000);
- }
+ lightbar_off();
+ WAIT_OR_RET(-1);
+ return 0;
+}
+/* CPU is off. The lightbar loses power when the CPU is in S5, so there's
+ * nothing to do. We'll just wait here until the state changes. */
+static uint32_t sequence_S5(void)
+{
+ WAIT_OR_RET(-1);
return 0;
}
/* Used by factory. */
static uint32_t sequence_TEST_inner(void)
{
- int i, j, k, r, g, b;
+ int i, k, r, g, b;
int kmax = 254;
int kstep = 8;
lightbar_init_vals();
lightbar_on();
- for (i = 0; i < ARRAY_SIZE(testy); i++) {
+ for (i = 0; i < ARRAY_SIZE(colors); i++) {
for (k = 0; k <= kmax; k += kstep) {
- for (j = 0; j < NUM_LEDS; j++) {
- r = testy[i].r ? k : 0;
- g = testy[i].g ? k : 0;
- b = testy[i].b ? k : 0;
- lightbar_setrgb(j, r, g, b);
- }
- WAIT_OR_RET(10000);
- }
- for (k = kmax; k >= 0; k -= kstep) {
- for (j = 0; j < NUM_LEDS; j++) {
- r = testy[i].r ? k : 0;
- g = testy[i].g ? k : 0;
- b = testy[i].b ? k : 0;
- lightbar_setrgb(j, r, g, b);
- }
- WAIT_OR_RET(10000);
+ r = colors[i].r ? k : 0;
+ g = colors[i].g ? k : 0;
+ b = colors[i].b ? k : 0;
+ lightbar_setrgb(NUM_LEDS, r, g, b);
}
+ WAIT_OR_RET(10000);
+ }
+ for (k = kmax; k >= 0; k -= kstep) {
+ r = colors[i].r ? k : 0;
+ g = colors[i].g ? k : 0;
+ b = colors[i].b ? k : 0;
+ lightbar_setrgb(NUM_LEDS, r, g, b);
+ WAIT_OR_RET(10000);
}
+ lightbar_setrgb(NUM_LEDS, r, g, b);
return 0;
}
@@ -695,6 +696,7 @@ static uint32_t sequence_TEST(void)
int tmp;
uint32_t r;
+ /* Force brightness to max, then restore it */
tmp = brightness;
brightness = 255;
r = sequence_TEST_inner();
@@ -749,7 +751,9 @@ static uint32_t sequence_STOP(void)
msg = TASK_EVENT_CUSTOM(task_wait_event(-1));
CPRINTF("[%T LB_stop got pending_msg %d]\n", pending_msg);
} while (msg != PENDING_MSG || pending_msg != LIGHTBAR_RUN);
- /* FIXME: What should we do if the host shuts down? */
+
+ /* Q: What should we do if the host shuts down? */
+ /* A: Nothing. We could be driving from the EC console. */
CPRINTF("[%T LB_stop->running]\n");
return 0;
@@ -858,6 +862,7 @@ static uint32_t sequence_KONAMI(void)
int i;
int tmp;
+ lightbar_off();
lightbar_init_vals();
lightbar_on();
@@ -892,7 +897,6 @@ static struct lightbar_cmd_t lightbar_cmds[] = {
};
#undef LBMSG
-
void lightbar_task(void)
{
uint32_t msg;
@@ -983,6 +987,7 @@ static int lightbar_shutdown(void)
}
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, lightbar_shutdown, HOOK_PRIO_DEFAULT);
+
/****************************************************************************/
/* Generic command-handling (should work the same for both console & LPC) */
/****************************************************************************/
@@ -1084,7 +1089,6 @@ DECLARE_HOST_COMMAND(EC_CMD_LIGHTBAR_CMD,
EC_VER_MASK(0));
-
/****************************************************************************/
/* EC console commands */
/****************************************************************************/
@@ -1134,7 +1138,7 @@ static int command_lightbar(int argc, char **argv)
uint8_t num;
struct ec_response_lightbar out;
- if (1 == argc) { /* no args = dump 'em all */
+ if (argc == 1) { /* no args = dump 'em all */
do_cmd_dump(&out);
for (i = 0; i < ARRAY_SIZE(dump_reglist); i++)
ccprintf(" %02x %02x %02x\n",
@@ -1145,40 +1149,47 @@ static int command_lightbar(int argc, char **argv)
return EC_SUCCESS;
}
- if (argc == 2 && !strcasecmp(argv[1], "init")) {
+ if (!strcasecmp(argv[1], "init")) {
lightbar_init_vals();
return EC_SUCCESS;
}
- if (argc == 2 && !strcasecmp(argv[1], "off")) {
+ if (!strcasecmp(argv[1], "off")) {
lightbar_off();
return EC_SUCCESS;
}
- if (argc == 2 && !strcasecmp(argv[1], "on")) {
+ if (!strcasecmp(argv[1], "on")) {
lightbar_on();
return EC_SUCCESS;
}
- if (argc == 3 && !strcasecmp(argv[1], "brightness")) {
+ if (!strcasecmp(argv[1], "brightness")) {
char *e;
- num = 0xff & strtoi(argv[2], &e, 16);
- lightbar_brightness(num);
+ if (argc > 2) {
+ num = 0xff & strtoi(argv[2], &e, 16);
+ lightbar_brightness(num);
+ }
+ ccprintf("brightness is %02x\n", brightness);
return EC_SUCCESS;
}
- if (argc == 3 && !strcasecmp(argv[1], "demo")) {
- if (!strcasecmp(argv[2], "on") || argv[2][0] == '1')
- demo_mode = 1;
- else if (!strcasecmp(argv[2], "off") || argv[2][0] == '0')
- demo_mode = 0;
- else
- return EC_ERROR_PARAM1;
+ if (!strcasecmp(argv[1], "demo")) {
+ if (argc > 2) {
+ if (!strcasecmp(argv[2], "on") ||
+ argv[2][0] == '1')
+ demo_mode = 1;
+ else if (!strcasecmp(argv[2], "off") ||
+ argv[2][0] == '0')
+ demo_mode = 0;
+ else
+ return EC_ERROR_PARAM1;
+ }
ccprintf("demo mode is %s\n", demo_mode ? "on" : "off");
return EC_SUCCESS;
}
- if (argc >= 2 && !strcasecmp(argv[1], "seq")) {
+ if (!strcasecmp(argv[1], "seq")) {
char *e;
uint8_t num;
if (argc == 2) {