summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/banshee/board.h14
-rw-r--r--board/banshee/led.c323
-rw-r--r--board/banshee/pwm.c16
-rw-r--r--common/led_pwm.c5
-rw-r--r--include/config.h6
-rw-r--r--util/config_allowed.txt1
-rw-r--r--zephyr/shim/include/config_chip.h5
7 files changed, 318 insertions, 52 deletions
diff --git a/board/banshee/board.h b/board/banshee/board.h
index 6b9f7cd748..3f1d8c7f65 100644
--- a/board/banshee/board.h
+++ b/board/banshee/board.h
@@ -32,17 +32,9 @@
#define CONFIG_KEYBOARD_CUSTOMIZATION
/* LED */
-#define CONFIG_LED_PWM
-#define CONFIG_LED_PWM_COUNT 4
-#undef CONFIG_LED_PWM_NEAR_FULL_COLOR
-#undef CONFIG_LED_PWM_SOC_ON_COLOR
-#undef CONFIG_LED_PWM_SOC_SUSPEND_COLOR
-#undef CONFIG_LED_PWM_LOW_BATT_COLOR
-#define CONFIG_LED_PWM_NEAR_FULL_COLOR EC_LED_COLOR_WHITE
-#define CONFIG_LED_PWM_SOC_ON_COLOR EC_LED_COLOR_WHITE
-#define CONFIG_LED_PWM_SOC_SUSPEND_COLOR EC_LED_COLOR_WHITE
-#define CONFIG_LED_PWM_LOW_BATT_COLOR EC_LED_COLOR_AMBER
-
+#define CONFIG_LED_PWM_COUNT 2
+#define CONFIG_LED_PWM_TASK_DISABLED
+#define CONFIG_CMD_LEDTEST
/* Sensors */
#define CONFIG_ACCELGYRO_LSM6DSO /* Base accel */
#define CONFIG_ACCEL_LSM6DSO_INT_EVENT \
diff --git a/board/banshee/led.c b/board/banshee/led.c
index 12c5c0bb69..e9a3f563cd 100644
--- a/board/banshee/led.c
+++ b/board/banshee/led.c
@@ -3,7 +3,8 @@
* found in the LICENSE file.
*/
-/* Brya specific PWM LED settings: there are 2 LEDs on each side of the board,
+/* Banshee specific PWM LED settings:
+ * there are 2 LEDs on each side of the board,
* each one can be controlled separately. The LED colors are white or amber,
* and the default behavior is tied to the charging process: both sides are
* amber while charging the battery and white when the battery is charged.
@@ -12,43 +13,70 @@
#include <stdint.h>
#include "common.h"
+#include "charge_manager.h"
+#include "charge_state.h"
#include "compile_time_macros.h"
#include "ec_commands.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "lid_switch.h"
+#include "led_common.h"
#include "led_pwm.h"
#include "pwm.h"
#include "util.h"
+#define LED_TICKS_PER_CYCLE 10
+#define LED_ON_TICKS 5
+
+#define BREATH_LIGHT_LENGTH 55
+#define BREATH_HOLD_LENGTH 50
+#define BREATH_OFF_LENGTH 200
+
+
const enum ec_led_id supported_led_ids[] = {
- EC_LED_ID_LEFT_LED,
- EC_LED_ID_RIGHT_LED,
+ EC_LED_ID_BATTERY_LED,
+ EC_LED_ID_POWER_LED,
+};
+
+
+enum breath_status {
+ BREATH_LIGHT_UP = 0,
+ BREATH_LIGHT_DOWN,
+ BREATH_HOLD,
+ BREATH_OFF,
};
+
const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids);
-/*
- * We only have a white and an amber LED, so setting any other color results in
- * both LEDs being off. Cap at 50% to save power.
- */
struct pwm_led_color_map led_color_map[EC_LED_COLOR_COUNT] = {
- /* Amber, White */
- [EC_LED_COLOR_RED] = { 0, 0 },
- [EC_LED_COLOR_GREEN] = { 0, 0 },
- [EC_LED_COLOR_BLUE] = { 0, 0 },
- [EC_LED_COLOR_YELLOW] = { 0, 0 },
- [EC_LED_COLOR_WHITE] = { 0, 50 },
- [EC_LED_COLOR_AMBER] = { 50, 0 },
+ /* Red, Green, Blue */
+ [EC_LED_COLOR_RED] = { 50, 0, 0 },
+ [EC_LED_COLOR_GREEN] = { 0, 50, 0 },
+ [EC_LED_COLOR_BLUE] = { 0, 0, 8 },
+ [EC_LED_COLOR_YELLOW] = { 40, 50, 0 },
+ [EC_LED_COLOR_WHITE] = { 20, 50, 25 },
+ [EC_LED_COLOR_AMBER] = { 45, 5, 0 },
+};
+
+struct pwm_led_color_map pwr_led_color_map[EC_LED_COLOR_COUNT] = {
+ /* White, Green, Red */
+ [EC_LED_COLOR_WHITE] = { BREATH_LIGHT_LENGTH, 0, 0 },
};
-/* Two logical LEDs with amber and white channels. */
+/*
+ * Three logical LEDs with red、 blue、green channels
+ * and one logical LED with white channels.
+ */
struct pwm_led pwm_leds[CONFIG_LED_PWM_COUNT] = {
- {
+ [PWM_LED0] = {
.ch0 = PWM_CH_SIDE_LED_R,
.ch1 = PWM_CH_SIDE_LED_G,
.ch2 = PWM_CH_SIDE_LED_B,
.enable = &pwm_enable,
.set_duty = &pwm_set_duty,
},
- {
+ [PWM_LED1] = {
.ch0 = PWM_CH_POWER_LED_W,
.ch1 = PWM_LED_NO_CHANNEL,
.ch2 = PWM_LED_NO_CHANNEL,
@@ -57,12 +85,134 @@ struct pwm_led pwm_leds[CONFIG_LED_PWM_COUNT] = {
},
};
+
+uint8_t breath_led_light_up;
+uint8_t breath_led_light_down;
+uint8_t breath_led_hold;
+uint8_t breath_led_off;
+
+
+int breath_pwm_enable;
+int breath_led_status;
+static void breath_led_pwm_deferred(void);
+DECLARE_DEFERRED(breath_led_pwm_deferred);
+
+/*
+ * Breath LED API
+ * Max duty (percentage) = BREATH_LIGHT_LENGTH (55%)
+ * Fade time (second) = 550ms(In) / 550ms(Out)
+ * Duration time (second) = BREATH_HOLD_LENGTH(500ms)
+ * Interval time (second) = BREATH_OFF_LENGTH(2000ms)
+ */
+
+static void breath_led_pwm_deferred(void)
+{
+
+ switch (breath_led_status) {
+ case BREATH_LIGHT_UP:
+
+ if (breath_led_light_up <= BREATH_LIGHT_LENGTH)
+ pwm_set_duty(PWM_CH_POWER_LED_W,
+ breath_led_light_up++);
+ else {
+ breath_led_light_up = 0;
+ breath_led_light_down = BREATH_LIGHT_LENGTH;
+ breath_led_status = BREATH_HOLD;
+ }
+
+ break;
+ case BREATH_HOLD:
+
+ if (breath_led_hold <= BREATH_HOLD_LENGTH)
+ breath_led_hold++;
+ else {
+ breath_led_hold = 0;
+ breath_led_status = BREATH_LIGHT_DOWN;
+ }
+
+ break;
+ case BREATH_LIGHT_DOWN:
+
+ if (breath_led_light_down != 0)
+ pwm_set_duty(PWM_CH_POWER_LED_W,
+ breath_led_light_down--);
+ else {
+ breath_led_light_down = BREATH_LIGHT_LENGTH;
+ breath_led_status = BREATH_OFF;
+ }
+
+ break;
+ case BREATH_OFF:
+
+ if (breath_led_off <= BREATH_OFF_LENGTH)
+ breath_led_off++;
+ else {
+ breath_led_off = 0;
+ breath_led_status = BREATH_LIGHT_UP;
+ }
+
+ break;
+ }
+
+
+ if (breath_pwm_enable)
+ hook_call_deferred(&breath_led_pwm_deferred_data, 10 * MSEC);
+}
+
+
+void breath_led_run(uint8_t enable)
+{
+ if (enable && !breath_pwm_enable) {
+ breath_pwm_enable = true;
+ breath_led_status = BREATH_LIGHT_UP;
+ hook_call_deferred(&breath_led_pwm_deferred_data, 10 * MSEC);
+ } else if (!enable && breath_pwm_enable) {
+ breath_pwm_enable = false;
+ breath_led_light_up = 0;
+ breath_led_light_down = 0;
+ breath_led_hold = 0;
+ breath_led_off = 0;
+ breath_led_status = BREATH_OFF;
+ hook_call_deferred(&breath_led_pwm_deferred_data, -1);
+ }
+}
+
+
void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range)
{
- memset(brightness_range, '\0',
- sizeof(*brightness_range) * EC_LED_COLOR_COUNT);
- brightness_range[EC_LED_COLOR_AMBER] = 100;
- brightness_range[EC_LED_COLOR_WHITE] = 100;
+ if (led_id == EC_LED_ID_BATTERY_LED) {
+ brightness_range[EC_LED_COLOR_RED] = 100;
+ brightness_range[EC_LED_COLOR_GREEN] = 100;
+ brightness_range[EC_LED_COLOR_YELLOW] = 100;
+ brightness_range[EC_LED_COLOR_AMBER] = 100;
+ brightness_range[EC_LED_COLOR_BLUE] = 100;
+ brightness_range[EC_LED_COLOR_WHITE] = 100;
+ } else if (led_id == EC_LED_ID_POWER_LED)
+ brightness_range[EC_LED_COLOR_WHITE] = 100;
+
+}
+
+void set_pwr_led_color(enum pwm_led_id id, int color)
+{
+ struct pwm_led duty = { 0 };
+ const struct pwm_led *led = &pwm_leds[id];
+
+ if ((id >= CONFIG_LED_PWM_COUNT) || (id < 0) ||
+ (color >= EC_LED_COLOR_COUNT) || (color < -1))
+ return;
+
+ if (color != -1) {
+ duty.ch0 = pwr_led_color_map[color].ch0;
+ duty.ch1 = pwr_led_color_map[color].ch1;
+ duty.ch2 = pwr_led_color_map[color].ch2;
+ }
+
+ if (led->ch0 != (enum pwm_channel)PWM_LED_NO_CHANNEL)
+ led->set_duty(led->ch0, duty.ch0);
+ if (led->ch1 != (enum pwm_channel)PWM_LED_NO_CHANNEL)
+ led->set_duty(led->ch1, duty.ch1);
+ if (led->ch2 != (enum pwm_channel)PWM_LED_NO_CHANNEL)
+ led->set_duty(led->ch2, duty.ch2);
}
int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness)
@@ -70,24 +220,131 @@ int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness)
enum pwm_led_id pwm_id;
/* Convert ec_led_id to pwm_led_id. */
- switch (led_id) {
- case EC_LED_ID_LEFT_LED:
+ if (led_id == EC_LED_ID_BATTERY_LED)
pwm_id = PWM_LED0;
- break;
- case EC_LED_ID_RIGHT_LED:
+ else if (led_id == EC_LED_ID_POWER_LED)
pwm_id = PWM_LED1;
+ else
+ return EC_ERROR_UNKNOWN;
+
+ if (led_id == EC_LED_ID_POWER_LED) {
+ if (brightness[EC_LED_COLOR_WHITE])
+ set_pwr_led_color(pwm_id, EC_LED_COLOR_WHITE);
+ else
+ /* Otherwise, the "color" is "off". */
+ set_pwr_led_color(pwm_id, -1);
+ } else {
+ if (brightness[EC_LED_COLOR_RED])
+ set_pwm_led_color(pwm_id, EC_LED_COLOR_RED);
+ else if (brightness[EC_LED_COLOR_GREEN])
+ set_pwm_led_color(pwm_id, EC_LED_COLOR_GREEN);
+ else if (brightness[EC_LED_COLOR_BLUE])
+ set_pwm_led_color(pwm_id, EC_LED_COLOR_BLUE);
+ else if (brightness[EC_LED_COLOR_YELLOW])
+ set_pwm_led_color(pwm_id, EC_LED_COLOR_YELLOW);
+ else if (brightness[EC_LED_COLOR_WHITE])
+ set_pwm_led_color(pwm_id, EC_LED_COLOR_WHITE);
+ else if (brightness[EC_LED_COLOR_AMBER])
+ set_pwm_led_color(pwm_id, EC_LED_COLOR_AMBER);
+ else
+ /* Otherwise, the "color" is "off". */
+ set_pwm_led_color(pwm_id, -1);
+ }
+
+ return EC_SUCCESS;
+}
+
+static void select_active_port_led(int port)
+{
+ if ((port == USBC_PORT_C0) || (port == USBC_PORT_C1)) {
+ gpio_set_level(GPIO_LEFT_SIDE, 0);
+ gpio_set_level(GPIO_RIGHT_SIDE, 1);
+ } else if ((port == USBC_PORT_C2) || (port == USBC_PORT_C3)) {
+ gpio_set_level(GPIO_LEFT_SIDE, 1);
+ gpio_set_level(GPIO_RIGHT_SIDE, 0);
+ } else if ((charge_get_state() == PWR_STATE_DISCHARGE &&
+ charge_get_percent() < 10) ||
+ charge_get_state() == PWR_STATE_ERROR) {
+ gpio_set_level(GPIO_LEFT_SIDE, 1);
+ gpio_set_level(GPIO_RIGHT_SIDE, 1);
+ } else {
+ gpio_set_level(GPIO_LEFT_SIDE, 0);
+ gpio_set_level(GPIO_RIGHT_SIDE, 0);
+ }
+}
+
+static void set_active_port_color(int color)
+{
+ int port = charge_manager_get_active_charge_port();
+
+ if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) {
+ select_active_port_led(port);
+ set_pwm_led_color(PWM_LED0, port ? color : -1);
+ }
+}
+
+static void led_set_battery(void)
+{
+ static int battery_ticks;
+ uint32_t chflags = charge_get_flags();
+
+ battery_ticks++;
+
+ switch (charge_get_state()) {
+ case PWR_STATE_CHARGE:
+ /* Always indicate when charging, even in suspend. */
+ set_active_port_color(EC_LED_COLOR_AMBER);
+ break;
+ case PWR_STATE_DISCHARGE:
+ if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) {
+ if (charge_get_percent() < 10)
+ set_active_port_color((battery_ticks & 0x2) ?
+ EC_LED_COLOR_RED : -1);
+ else
+ set_active_port_color(-1);
+ }
+ break;
+ case PWR_STATE_ERROR:
+ set_active_port_color((battery_ticks & 0x2) ?
+ EC_LED_COLOR_RED : -1);
+ case PWR_STATE_CHARGE_NEAR_FULL:
+ set_active_port_color(EC_LED_COLOR_GREEN);
+ break;
+ case PWR_STATE_IDLE:
+ if (chflags & CHARGE_FLAG_FORCE_IDLE)
+ set_active_port_color((battery_ticks & 0x4) ?
+ EC_LED_COLOR_AMBER : -1);
+ else
+ set_active_port_color(EC_LED_COLOR_AMBER);
break;
default:
- return EC_ERROR_UNKNOWN;
+ break;
}
- if (brightness[EC_LED_COLOR_WHITE])
- set_pwm_led_color(pwm_id, EC_LED_COLOR_WHITE);
- else if (brightness[EC_LED_COLOR_AMBER])
- set_pwm_led_color(pwm_id, EC_LED_COLOR_AMBER);
+}
+
+static void led_set_power(void)
+{
+ if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) {
+ breath_led_run(1);
+ return;
+ }
else
- /* Otherwise, the "color" is "off". */
- set_pwm_led_color(pwm_id, -1);
+ breath_led_run(0);
+
+ if (chipset_in_state(CHIPSET_STATE_ON)) {
+ set_pwr_led_color(PWM_LED1, EC_LED_COLOR_WHITE);
+ } else
+ set_pwr_led_color(PWM_LED1, -1);
+}
+
+/* Called by hook task every TICK */
+static void led_tick(void)
+{
+ if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED))
+ led_set_battery();
+ if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED))
+ led_set_power();
- return EC_SUCCESS;
}
+DECLARE_HOOK(HOOK_TICK, led_tick, HOOK_PRIO_DEFAULT);
diff --git a/board/banshee/pwm.c b/board/banshee/pwm.c
index 618f6063f6..127aca3901 100644
--- a/board/banshee/pwm.c
+++ b/board/banshee/pwm.c
@@ -13,18 +13,18 @@
const struct pwm_t pwm_channels[] = {
[PWM_CH_SIDE_LED_R] = {
.channel = 0,
- .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP,
- .freq = 4800,
+ .flags = PWM_CONFIG_DSLEEP,
+ .freq = 2000,
},
[PWM_CH_SIDE_LED_G] = {
.channel = 1,
- .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP,
- .freq = 4800,
+ .flags = PWM_CONFIG_DSLEEP,
+ .freq = 2000,
},
[PWM_CH_SIDE_LED_B] = {
.channel = 2,
- .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP,
- .freq = 4800,
+ .flags = PWM_CONFIG_DSLEEP,
+ .freq = 2000,
},
[PWM_CH_KBLIGHT] = {
.channel = 3,
@@ -44,8 +44,8 @@ const struct pwm_t pwm_channels[] = {
},
[PWM_CH_POWER_LED_W] = {
.channel = 7,
- .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP,
- .freq = 4800,
+ .flags = PWM_CONFIG_DSLEEP,
+ .freq = 2000,
},
};
BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
diff --git a/common/led_pwm.c b/common/led_pwm.c
index b4f42bb37d..0150e73382 100644
--- a/common/led_pwm.c
+++ b/common/led_pwm.c
@@ -31,7 +31,9 @@
#define PULSE_TICK (250 * MSEC)
+#ifndef CONFIG_LED_PWM_TASK_DISABLED
static uint8_t led_is_pulsing;
+#endif /* CONFIG_LED_PWM_TASK_DISABLED */
static int get_led_id_color(enum pwm_led_id id, int color)
{
@@ -122,6 +124,7 @@ static void init_leds_off(void)
}
DECLARE_HOOK(HOOK_INIT, init_leds_off, HOOK_PRIO_INIT_PWM + 1);
+#ifndef CONFIG_LED_PWM_TASK_DISABLED
static uint8_t pulse_period;
static uint8_t pulse_ontime;
static enum ec_led_colors pulse_color;
@@ -259,6 +262,8 @@ static void update_leds(void)
}
DECLARE_HOOK(HOOK_TICK, update_leds, HOOK_PRIO_DEFAULT);
+#endif/* CONFIG_LED_PWM_TASK_DISABLED */
+
#ifdef CONFIG_CMD_LEDTEST
static int command_ledtest(int argc, char **argv)
{
diff --git a/include/config.h b/include/config.h
index 9cf522a98a..ebe7199cbe 100644
--- a/include/config.h
+++ b/include/config.h
@@ -3057,6 +3057,12 @@
#undef CONFIG_LED_PWM
/*
+ * Support common PWM-controlled LEDs that do not conform to the Chrom OS LED
+ * behavior specification
+ */
+#undef CONFIG_LED_PWM_TASK_DISABLED
+
+/*
* Here are some recommended color settings by default, but a board can change
* the colors to one of "enum ec_led_colors" as they see fit.
*/
diff --git a/util/config_allowed.txt b/util/config_allowed.txt
index 11e1e9ad0b..0223f08917 100644
--- a/util/config_allowed.txt
+++ b/util/config_allowed.txt
@@ -606,6 +606,7 @@ CONFIG_LED_POWER_LED
CONFIG_LED_PWM_ACTIVE_CHARGE_PORT_ONLY
CONFIG_LED_PWM_CHARGE_STATE_ONLY
CONFIG_LED_PWM_COUNT
+CONFIG_LED_PWM_TASK_DISABLED
CONFIG_LFW_OFFSET
CONFIG_LIBCRYPTOC
CONFIG_LID_ANGLE_INVALID_CHECK
diff --git a/zephyr/shim/include/config_chip.h b/zephyr/shim/include/config_chip.h
index c313a9df15..5a6094fa38 100644
--- a/zephyr/shim/include/config_chip.h
+++ b/zephyr/shim/include/config_chip.h
@@ -722,6 +722,11 @@ extern struct jump_data mock_jump_data;
#define CONFIG_LED_PWM
#endif
+#undef CONFIG_LED_PWM_TASK_DISABLED
+#ifdef CONFIG_PLATFORM_EC_LED_PWM_TASK_DISABLED
+#define CONFIG_LED_PWM_TASK_DISABLED
+#endif
+
#undef CONFIG_LED_PWM_COUNT
#if DT_HAS_COMPAT_STATUS_OKAY(cros_ec_pwm_leds)
#define CONFIG_LED_PWM_COUNT DT_PROP_LEN(DT_INST(0, cros_ec_pwm_leds), leds)