diff --git a/board/eve/board.c b/board/eve/board.c index 695909d7fb..85f82b6932 100644 --- a/board/eve/board.c +++ b/board/eve/board.c @@ -733,6 +733,7 @@ void sensor_board_proc_double_tap(void) { /* TODO: Call led update function */ CPRINTS("Call LED status update"); + led_register_double_tap(); } /* Base Sensor mutex */ diff --git a/board/eve/board.h b/board/eve/board.h index 29967c07ef..555300ffcf 100644 --- a/board/eve/board.h +++ b/board/eve/board.h @@ -298,6 +298,7 @@ enum adc_channel { int board_get_version(void); void board_reset_pd_mcu(void); void board_set_tcpc_power_mode(int port, int mode); +void led_register_double_tap(void); /* Sensors without hardware FIFO are in forced mode */ #define CONFIG_ACCEL_FORCE_MODE_MASK ((1 << LID_ACCEL) | (1 << LID_LIGHT)) diff --git a/board/eve/led.c b/board/eve/led.c index 2b7c1cfd7f..cdda42d07a 100644 --- a/board/eve/led.c +++ b/board/eve/led.c @@ -20,43 +20,118 @@ #define CPRINTF(format, args...) cprintf(CC_PWM, format, ## args) #define CPRINTS(format, args...) cprints(CC_PWM, format, ## args) -#define LED_TOTAL_TICKS 16 -#define LED_ON_TICKS 8 +#define LED_TICKS_PER_BEAT 2 +#define LED_BEATS_PER_PHASE 2 +#define NUM_PHASE 2 +#define DOUBLE_TAP_TICK_LEN (LED_TICKS_PER_BEAT * 8) static int led_debug; +static int double_tap; +static int double_tap_tick_count; +static int led_pattern; +static int led_ticks; const enum ec_led_id supported_led_ids[] = { EC_LED_ID_LEFT_LED, EC_LED_ID_RIGHT_LED}; const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); +/* List of LED colors used */ enum led_color { LED_OFF = 0, LED_RED, LED_GREEN, LED_BLUE, LED_WHITE, - LED_AMBER, + LED_RED_2_3, + LED_RED_1_3, /* Number of colors, not a color itself */ LED_COLOR_COUNT }; +/* List of supported LED patterns */ +enum led_pattern { + SOLID_GREEN = 0, + WHITE_GREEN, + SOLID_WHITE, + WHITE_RED, + SOLID_RED, + PULSE_RED_1, + PULSE_RED_2, + BLINK_RED, + OFF, + LED_NUM_PATTERNS, +}; + enum led_side { LED_LEFT = 0, LED_RIGHT, LED_BOTH }; -/* Brightness vs. color, in the order of off, red, green and blue */ +/* + * LED patterns are described as two phases. Each phase has an associated LED + * color and length in beats. The length of each beat is defined by the macro + * LED_TICKS_PER_BEAT. + */ +struct led_phase { + uint8_t color[NUM_PHASE]; + uint8_t len[NUM_PHASE]; +}; + +/* + * Pattern table. The len field is beats per color. 0 for len indicates that a + * particular pattern never changes from the first phase. + */ +static const struct led_phase pattern[LED_NUM_PATTERNS] = { + { {LED_GREEN, LED_GREEN}, {0, 0} }, + { {LED_WHITE, LED_GREEN}, {2, 4} }, + { {LED_WHITE, LED_WHITE}, {0, 0} }, + { {LED_WHITE, LED_RED}, {2, 4} }, + { {LED_RED, LED_RED}, {0, 0} }, + { {LED_RED, LED_RED_2_3}, {4, 4} }, + { {LED_RED, LED_RED_1_3}, {2, 4} }, + { {LED_RED, LED_OFF}, {1, 6} }, + { {LED_OFF, LED_OFF}, {0, 0} }, +}; + +/* + * Brightness vs. color, in the order of off, red, green and blue. Values are + * for % on PWM duty cycle time. + */ #define PWM_CHAN_PER_LED 3 static const uint8_t color_brightness[LED_COLOR_COUNT][PWM_CHAN_PER_LED] = { /* {Red, Green, Blue}, */ - [LED_OFF] = {100, 100, 100}, - [LED_RED] = {20, 100, 100}, - [LED_GREEN] = {100, 20, 100}, - [LED_BLUE] = {100, 100, 20}, - [LED_WHITE] = {0, 0, 0}, - [LED_AMBER] = {0, 87, 100}, + [LED_OFF] = {0, 0, 0}, + [LED_RED] = {80, 0, 0}, + [LED_GREEN] = {0, 80, 0}, + [LED_BLUE] = {0, 0, 80}, + [LED_WHITE] = {100, 100, 100}, + [LED_RED_2_3] = {53, 0, 0}, + [LED_RED_1_3] = {27, 0, 0}, +}; + +/* + * When a double tap event occurs, a LED pattern is displayed based on the + * current battery charge level. The LED patterns used for double tap under low + * battery conditions are same patterns displayed when the battery is not + * charging. The table below shows what battery charge level displays which + * pattern. + */ +struct range_map { + uint8_t max; + uint8_t pattern; +}; + +static const struct range_map pattern_tbl[] = { + {2, BLINK_RED}, + {4, PULSE_RED_2}, + {10, PULSE_RED_1}, + {15, SOLID_RED}, + {30, WHITE_RED}, + {90, SOLID_WHITE}, + {98, WHITE_GREEN}, + {100, SOLID_GREEN}, }; /** @@ -76,7 +151,7 @@ static void set_color(enum led_color color, enum led_side side) if (saved_duty[LED_LEFT][i] != color_brightness[color][i]) { pwm_set_duty(PWM_CH_LED_L_RED + i, - color_brightness[color][i]); + 100 - color_brightness[color][i]); saved_duty[LED_LEFT][i] = color_brightness[color][i]; } @@ -89,7 +164,7 @@ static void set_color(enum led_color color, enum led_side side) if (saved_duty[LED_RIGHT][i] != color_brightness[color][i]) { pwm_set_duty(PWM_CH_LED_R_RED + i, - color_brightness[color][i]); + 100 - color_brightness[color][i]); saved_duty[LED_RIGHT][i] = color_brightness[color][i]; } @@ -131,107 +206,112 @@ int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) return EC_SUCCESS; } +void led_register_double_tap(void) +{ + double_tap = 1; +} + +static void led_manage_pattern(int side) +{ + int color; + int phase; + + /* Determine pattern phase */ + phase = led_ticks < LED_TICKS_PER_BEAT * pattern[led_pattern].len[0] ? + 0 : 1; + color = pattern[led_pattern].color[phase]; + /* Set color for the current phase */ + set_color(color, side); + + /* + * Update led_ticks. If the len field is 0, then the pattern + * being used is just one color so no need to increase the tick count. + */ + if (pattern[led_pattern].len[0]) + if (++led_ticks == LED_TICKS_PER_BEAT * + (pattern[led_pattern].len[0] + + pattern[led_pattern].len[1])) + led_ticks = 0; + + /* If double tap display is active, decrement its counter */ + if (double_tap_tick_count) + double_tap_tick_count--; +} + static void eve_led_set_power_battery(void) { - static int power_ticks; enum charge_state chg_state = charge_get_state(); int side; + int percent_chg; + enum led_pattern pattern; + int tap = 0; + if (double_tap) { + /* Clear double tap indication */ + if (!chipset_in_state(CHIPSET_STATE_ON)) + /* If not in S0, then set tap on */ + tap = 1; + double_tap = 0; + } /* Get active charge port which maps directly to left/right LED */ side = charge_manager_get_active_charge_port(); /* Ensure that side can be safely used as an index */ if (side < 0 || side >= CONFIG_USB_PD_PORT_COUNT) side = LED_BOTH; - if (chipset_in_state(CHIPSET_STATE_ON)) { - enum led_side blueside = LED_BOTH; - - if (chg_state == PWR_STATE_CHARGE) { - set_color(LED_AMBER, side); - blueside = !side; - } else if (chg_state == PWR_STATE_IDLE || chg_state == - PWR_STATE_CHARGE_NEAR_FULL) { - set_color(LED_GREEN, side); - blueside = !side; - } else if (chg_state == PWR_STATE_DISCHARGE_FULL && - extpower_is_present()) { - set_color(LED_GREEN, side); - blueside = !side; - } - set_color(LED_BLUE, blueside); - return; - } - - /* Flash red on critical battery, which usually inhibits AP power-on. */ - if (battery_is_present() != BP_YES || - charge_get_percent() < CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON) { - set_color(((power_ticks++ % LED_TOTAL_TICKS) < LED_ON_TICKS) ? - LED_RED : LED_OFF, LED_BOTH); - return; - } - - /* Suspend or Standby state */ - if (chipset_in_state(CHIPSET_STATE_SUSPEND) || - chipset_in_state(CHIPSET_STATE_STANDBY)) { - enum led_side blinkside = LED_BOTH; + /* Get percent charge */ + percent_chg = charge_get_percent(); + /* + * If a double tap update is underway, let that complete before allowing + * the pattern to change. + */ + if (!double_tap_tick_count) { if (chg_state == PWR_STATE_CHARGE_NEAR_FULL || - chg_state == PWR_STATE_IDLE) { - set_color(LED_GREEN, side); - blinkside = !side; + ((chg_state == PWR_STATE_DISCHARGE_FULL) + && extpower_is_present())) { + pattern = SOLID_GREEN; } else if (chg_state == PWR_STATE_CHARGE) { - set_color(LED_AMBER, side); - blinkside = !side; - } else if (chg_state == PWR_STATE_DISCHARGE_FULL && - extpower_is_present()) { - set_color(LED_GREEN, side); - blinkside = !side; - } - if (chg_state == PWR_STATE_DISCHARGE || - chg_state == PWR_STATE_DISCHARGE_FULL || - chg_state == PWR_STATE_CHARGE || - chg_state == PWR_STATE_CHARGE_NEAR_FULL || - chg_state == PWR_STATE_IDLE) { + pattern = SOLID_WHITE; + } else { + int i; + /* - * If in S3/S0iX and not in some error - * state, then flash non-charging LEDs white. + * Not currently charging. Select the pattern based on + * the battery charge level. If there is no double tap + * event to process, then only the low battery patterns + * are relevant. */ - set_color(((power_ticks++ % LED_TOTAL_TICKS) < - LED_ON_TICKS) ? - LED_WHITE : LED_OFF, blinkside); - return; + for (i = 0; i < ARRAY_SIZE(pattern_tbl); i++) { + if (percent_chg <= pattern_tbl[i].max) { + pattern = pattern_tbl[i].pattern; + break; + } + } + /* + * The patterns used for double tap and for not charging + * state are the same for low battery cases. But, if + * battery charge is high enough to be above SOLID_RED, + * then only display LED pattern if double tap has + * occurred. + */ + if (tap == 0 && pattern <= WHITE_RED) + pattern = OFF; + else + /* Start double tap LED sequence */ + double_tap_tick_count = DOUBLE_TAP_TICK_LEN; + } + + /* If the LED pattern will change, then reset tick count and set + * new pattern. + */ + if (pattern != led_pattern) { + led_ticks = 0; + led_pattern = pattern; } } - /* CHIPSET_STATE_OFF */ - switch (chg_state) { - case PWR_STATE_DISCHARGE_FULL: - set_color(LED_OFF, LED_BOTH); - if (extpower_is_present()) - set_color(LED_GREEN, side); - break; - case PWR_STATE_DISCHARGE: - set_color(LED_OFF, LED_BOTH); - break; - case PWR_STATE_CHARGE: - set_color(LED_OFF, LED_BOTH); - set_color(LED_AMBER, side); - break; - case PWR_STATE_ERROR: - set_color(((power_ticks++ % LED_TOTAL_TICKS) - < LED_ON_TICKS) ? LED_RED : LED_GREEN, LED_BOTH); - break; - case PWR_STATE_CHARGE_NEAR_FULL: - case PWR_STATE_IDLE: /* External power connected in IDLE. */ - set_color(LED_OFF, LED_BOTH); - set_color(LED_GREEN, side); - break; - default: - set_color(LED_RED, LED_BOTH); - break; - } - if (chg_state != PWR_STATE_ERROR) - power_ticks = 0; + led_manage_pattern(side); } static void led_init(void) @@ -251,6 +331,9 @@ static void led_init(void) pwm_enable(PWM_CH_LED_R_BLUE, 1); set_color(LED_OFF, LED_BOTH); + led_pattern = OFF; + led_ticks = 0; + double_tap_tick_count = 0; } /* After pwm_pin_init() */ DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT); @@ -260,7 +343,7 @@ DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT); */ static void led_tick(void) { - if (led_debug) + if (led_debug == 1) return; if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED) && @@ -300,8 +383,6 @@ static int command_led(int argc, char **argv) set_color(LED_BLUE, side); } else if (!strcasecmp(argv[1], "white")) { set_color(LED_WHITE, side); - } else if (!strcasecmp(argv[1], "amber")) { - set_color(LED_AMBER, side); } else { /* maybe handle charger_discharge_on_ac() too? */ return EC_ERROR_PARAM1;