diff options
author | Daisuke Nojiri <dnojiri@chromium.org> | 2021-12-04 13:08:04 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2022-03-01 19:41:59 +0000 |
commit | 14b34644fe16349af348d21f20d3897707132896 (patch) | |
tree | 1439a332e77c942d809b2ebfc128d690d72fe8e8 | |
parent | 10a7a0a72017fe15633fa563977b748e88ec8726 (diff) | |
download | chrome-ec-14b34644fe16349af348d21f20d3897707132896.tar.gz |
RGBKBD: Add RGB Keyboard task
This patch introduces a RGB keyboard task and a IS31FL3743B driver.
BUG=b:203664745, b:213921985, b:199995751
BRANCH=None
TEST=Vell and unit test.
Change-Id: Iefc1714efca9a4dc70db5a024d1ab020ec7b69b6
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3342506
Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
Reviewed-by: Aaron Massey <aaronmassey@google.com>
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/rgb_keyboard.c | 315 | ||||
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/led/is31fl3743b.c | 189 | ||||
-rw-r--r-- | include/config.h | 24 | ||||
-rw-r--r-- | include/console_channel.inc | 3 | ||||
-rw-r--r-- | include/ec_commands.h | 15 | ||||
-rw-r--r-- | include/rgb_keyboard.h | 121 | ||||
-rw-r--r-- | test/build.mk | 2 | ||||
-rw-r--r-- | test/rgb_keyboard.c | 348 | ||||
-rw-r--r-- | test/rgb_keyboard.tasklist | 10 | ||||
-rw-r--r-- | test/test_config.h | 5 | ||||
-rw-r--r-- | zephyr/CMakeLists.txt | 2 | ||||
-rw-r--r-- | zephyr/Kconfig.keyboard | 49 |
14 files changed, 1085 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk index 25dbce2333..3c5c90faff 100644 --- a/common/build.mk +++ b/common/build.mk @@ -133,6 +133,7 @@ common-$(CONFIG_PSTORE)+=pstore_commands.o common-$(CONFIG_PWM)+=pwm.o common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o common-$(CONFIG_KEYBOARD_BACKLIGHT)+=keyboard_backlight.o +common-$(CONFIG_RGB_KEYBOARD)+=rgb_keyboard.o common-$(CONFIG_RSA)+=rsa.o common-$(CONFIG_ROLLBACK)+=rollback.o common-$(CONFIG_RWSIG)+=rwsig.o vboot/common.o diff --git a/common/rgb_keyboard.c b/common/rgb_keyboard.c new file mode 100644 index 0000000000..2a2e05b43a --- /dev/null +++ b/common/rgb_keyboard.c @@ -0,0 +1,315 @@ +/* Copyright 2022 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include <stdbool.h> + +#include "atomic.h" +#include "common.h" +#include "console.h" +#include "ec_commands.h" +#include "gpio.h" +#include "hooks.h" +#include "registers.h" +#include "rgb_keyboard.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_RGBKBD, outstr) +#define CPRINTF(fmt, args...) cprintf(CC_RGBKBD, "RGBKBD: " fmt, ##args) +#define CPRINTS(fmt, args...) cprints(CC_RGBKBD, "RGBKBD: " fmt, ##args) + +test_export_static enum rgbkbd_demo demo = +#if defined(CONFIG_RGBKBD_DEMO_FLOW) + RGBKBD_DEMO_FLOW +#elif defined(CONFIG_RGBKBD_DEMO_DOT) + RGBKBD_DEMO_DOT +#else + RGBKBD_DEMO_OFF +#endif + ; + +static int set_color_single(struct rgb_s color, int x, int y) +{ + struct rgbkbd *ctx = &rgbkbds[0]; + uint8_t gid; + uint8_t col = 0; + uint8_t offset; + + if (rgbkbd_hsize <= x || rgbkbd_vsize <= y) { + return EC_ERROR_OVERFLOW; + } + + /* Search the grid where x belongs to. */ + for (gid = 0; gid < rgbkbd_count; gid++, ctx++) { + if (x < col + ctx->cfg->col_len) + break; + col += ctx->cfg->col_len; + } + + offset = ctx->cfg->row_len * (x - col) + y; + ctx->buf[offset] = color; + + CPRINTS("Set [%d,%d] to color=[%d,%d,%d] (gid=%u offset=%u)", + x, y, color.r, color.g, color.b, gid, offset); + return ctx->cfg->drv->set_color(ctx, offset, &ctx->buf[offset], 1); +} + +test_export_static uint8_t get_grid_size(const struct rgbkbd *ctx) +{ + return ctx->cfg->col_len * ctx->cfg->row_len; +} + +static void sync_grids(void) +{ + struct rgbkbd *ctx; + uint8_t len; + int i; + + for (i = 0; i < rgbkbd_count; i++) { + ctx = &rgbkbds[i]; + len = get_grid_size(ctx); + ctx->cfg->drv->set_color(ctx, 0, ctx->buf, len); + } +} + +test_export_static struct rgb_s rotate_color(struct rgb_s color, int step) +{ + color.r += step; + if (color.r == 0) { + color.g += step; + if (color.g == 0) { + color.b += step; + } + } + + return color; +} + +static void rgbkbd_reset_color(struct rgb_s color) +{ + struct rgbkbd *ctx; + int i, j; + + for (i = 0; i < rgbkbd_count; i++) { + ctx = &rgbkbds[i]; + for (j = 0; j < get_grid_size(ctx); j++) + ctx->buf[j] = color; + } + + sync_grids(); +} + +static void rgbkbd_demo_flow(void) +{ + struct rgbkbd *ctx = &rgbkbds[0]; + static struct rgb_s color; + uint8_t len; + int i, g; + + for (g = rgbkbd_count - 1; g >= 0; g--) { + ctx = &rgbkbds[g]; + len = get_grid_size(ctx); + for (i = len - 1; i > 0; i--) + ctx->buf[i] = ctx->buf[i - 1]; + if (g > 0) { + /* Copy the last dot of the g-1 grid to the 1st. */ + len = get_grid_size(&rgbkbds[g - 1]); + ctx->buf[0] = rgbkbds[g - 1].buf[len - 1]; + } + } + + /* Create a new color by shifting R by <step>. */ + color = rotate_color(color, 32); + + /* Finally, insert a new color to (0, 0). */ + ctx->buf[0] = color; + + sync_grids(); + +#ifdef TEST_BUILD + task_wake(TASK_ID_TEST_RUNNER); +#else + msleep(250); +#endif +} + +static void rgbkbd_demo_dot(void) +{ + static struct rgb_s color = { 0x80, 0, 0 }; + const struct rgb_s off = { 0, 0, 0 }; + static uint8_t x, y; + + /* Turn off previous dot. */ + set_color_single(off, x, y); + + /* Move position. */ + y++; + if (y >= rgbkbd_vsize) { + y = 0; + x++; + if (x >= rgbkbd_hsize) { + x = 0; + color = rotate_color(color, 0x80); + } + } + + /* Turn on next dot. */ + set_color_single(color, x, y); + +#ifdef TEST_BUILD + task_wake(TASK_ID_TEST_RUNNER); +#else + msleep(250); +#endif +} + +static void rgbkbd_demo(enum rgbkbd_demo id) +{ + switch (id) { + case RGBKBD_DEMO_FLOW: + rgbkbd_demo_flow(); + break; + case RGBKBD_DEMO_DOT: + rgbkbd_demo_dot(); + break; + case RGBKBD_DEMO_OFF: + default: + break; + } +} + +__overridable void board_enable_rgb_keyboard(bool enable) {} + +void rgbkbd_task(void *u) +{ + uint32_t event; + int i, rv; + + board_enable_rgb_keyboard(true); + + for (i = 0; i < rgbkbd_count; i++) { + struct rgbkbd *ctx = &rgbkbds[i]; + rv = ctx->cfg->drv->init(ctx); + if (rv) + CPRINTS("Failed to init GRID%d (%d)", i, rv); + rv = ctx->cfg->drv->set_gcc(ctx, 0x80); + rv |= ctx->cfg->drv->set_scale(ctx, 0, 0x80, + get_grid_size(ctx)); + if (rv) + CPRINTS("Failed to set GCC or scale (%d)", rv); + } + + while (1) { + event = task_wait_event(100 * MSEC); + if (IS_ENABLED(CONFIG_RGB_KEYBOARD_DEBUG)) + CPRINTS("event=0x%08x", event); + if (demo) + rgbkbd_demo(demo); + } +} + +test_export_static int cc_rgbk(int argc, char **argv) +{ + struct rgbkbd *ctx; + char *end, *comma; + struct rgb_s color; + int gcc, x, y, val; + int i, rv = EC_SUCCESS; + + if (argc < 2 || 5 < argc) { + return EC_ERROR_PARAM_COUNT; + } + + comma = strstr(argv[1], ","); + if (comma && strlen(comma) > 1) { + /* Usage 2 */ + /* Found ',' and more string after that. Split it into two. */ + *comma = '\0'; + x = strtoi(argv[1], &end, 0); + if (*end || x >= rgbkbd_hsize) + return EC_ERROR_PARAM1; + y = strtoi(comma + 1, &end, 0); + if (*end || y >= rgbkbd_vsize) + return EC_ERROR_PARAM1; + } else if (!strcasecmp(argv[1], "all")) { + /* Usage 3 */ + x = -1; + y = -1; + } else if (!strcasecmp(argv[1], "demo")) { + /* Usage 4 */ + val = strtoi(argv[2], &end, 0); + if (*end || val >= RGBKBD_DEMO_COUNT) + return EC_ERROR_PARAM1; + demo = val; + rgbkbd_reset_color((struct rgb_s){.r = 0, .g = 0, .b = 0}); + ccprintf("Demo set to %d\n", demo); + return EC_SUCCESS; + } else { + /* Usage 1 */ + if (argc != 2) + return EC_ERROR_PARAM_COUNT; + gcc = strtoi(argv[1], &end, 0); + if (*end || gcc < 0 || gcc > UINT8_MAX) + return EC_ERROR_PARAM1; + demo = RGBKBD_DEMO_OFF; + for (i = 0; i < rgbkbd_count; i++) { + ctx = &rgbkbds[i]; + ctx->cfg->drv->set_gcc(ctx, gcc); + } + return EC_SUCCESS; + } + + if (argc != 5) + return EC_ERROR_PARAM_COUNT; + + val = strtoi(argv[2], &end, 0); + if (*end || val < 0 || val > UINT8_MAX) + return EC_ERROR_PARAM2; + color.r = val; + val = strtoi(argv[3], &end, 0); + if (*end || val < 0 || val > UINT8_MAX) + return EC_ERROR_PARAM3; + color.g = val; + val = strtoi(argv[4], &end, 0); + if (*end || val < 0 || val > UINT8_MAX) + return EC_ERROR_PARAM4; + color.b = val; + + demo = RGBKBD_DEMO_OFF; + if (y < 0 && x < 0) { + /* Usage 3 */ + rgbkbd_reset_color(color); + } else if (y < 0) { + /* Usage 2: Set all LEDs on column x. */ + ccprintf("Set column %d to 0x%02x%02x%02x\n", + x, color.r, color.g, color.b); + for (i = 0; i < rgbkbd_vsize; i++) + rv = set_color_single(color, x, i); + } else if (x < 0) { + /* Usage 2: Set all LEDs on row y. */ + ccprintf("Set row %d to 0x%02x%02x%02x\n", + y, color.r, color.g, color.b); + for (i = 0; i < rgbkbd_hsize; i++) + rv = set_color_single(color, i, y); + } else { + /* Usage 2 */ + ccprintf("Set (%d,%d) to 0x%02x%02x%02x\n", + x, y, color.r, color.g, color.b); + rv = set_color_single(color, x, y); + } + + return rv; +} +#ifndef TEST_BUILD +DECLARE_CONSOLE_COMMAND(rgbk, cc_rgbk, + "\n" + "1. rgbk <global-brightness>\n" + "2. rgbk <col,row> <r-bright> <g-bright> <b-bright>\n" + "3. rgbk all <r-bright> <g-bright> <b-bright>\n" + "4. rgbk demo <id>\n", + "Set color of RGB keyboard" + ); +#endif diff --git a/driver/build.mk b/driver/build.mk index ce32cda4b0..a1927e7e82 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -110,6 +110,7 @@ driver-$(CONFIG_LED_DRIVER_LM3630A)+=led/lm3630a.o driver-$(CONFIG_LED_DRIVER_LP5562)+=led/lp5562.o driver-$(CONFIG_LED_DRIVER_MP3385)+=led/mp3385.o driver-$(CONFIG_LED_DRIVER_OZ554)+=led/oz554.o +driver-$(CONFIG_LED_DRIVER_IS31FL3743B)+=led/is31fl3743b.o # 7-segment display driver-$(CONFIG_MAX695X_SEVEN_SEGMENT_DISPLAY)+=led/max695x.o diff --git a/driver/led/is31fl3743b.c b/driver/led/is31fl3743b.c new file mode 100644 index 0000000000..f1a8833318 --- /dev/null +++ b/driver/led/is31fl3743b.c @@ -0,0 +1,189 @@ +/* Copyright 2022 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include <string.h> + +#include "common.h" +#include "console.h" +#include "gpio.h" +#include "rgb_keyboard.h" +#include "spi.h" +#include "stddef.h" +#include "timer.h" + +#define CPRINTF(fmt, args...) cprintf(CC_RGBKBD, "RGBKBD: " fmt, ##args) +#define CPRINTS(fmt, args...) cprints(CC_RGBKBD, "RGBKBD: " fmt, ##args) + +#define SPI(id) (&(spi_devices[id])) + +#define IS31FL3743B_ROW_SIZE 6 +#define IS31FL3743B_COL_SIZE 11 +#define IS31FL3743B_GRID_SIZE (IS31FL3743B_COL_SIZE * IS31FL3743B_ROW_SIZE) +#define IS31FL3743B_BUF_SIZE (SIZE_OF_RGB * IS31FL3743B_GRID_SIZE) + +#define IS31FL3743B_CMD_ID 0b101 +#define IS31FL3743B_PAGE_PWM 0 +#define IS31FL3743B_PAGE_SCALE 1 +#define IS31FL3743B_PAGE_FUNC 2 + +#define IS31FL3743B_REG_CONFIG 0x00 +#define IS31FL3743B_REG_GCC 0x01 +#define IS31FL3743B_REG_PD_PU 0x02 +#define IS31FL3743B_REG_SPREAD_SPECTRUM 0x25 +#define IS31FL3743B_REG_RSTN 0x2f + +struct is31fl3743b_cmd { + uint8_t page: 4; + uint8_t id: 3; + uint8_t read: 1; +} __packed; + +struct is31fl3743b_msg { + struct is31fl3743b_cmd cmd; + uint8_t addr; + uint8_t payload[]; +} __packed; + +static int is31fl3743b_read(struct rgbkbd *ctx, uint8_t addr, uint8_t *value) +{ + uint8_t buf[8]; + struct is31fl3743b_msg *msg = (void *)buf; + const int frame_len = sizeof(*msg); + + msg->cmd.read = 1; + msg->cmd.id = IS31FL3743B_CMD_ID; + msg->cmd.page = IS31FL3743B_PAGE_FUNC; + msg->addr = addr; + + return spi_transaction(SPI(ctx->cfg->spi), buf, frame_len, value, 1); +} + +static int is31fl3743b_write(struct rgbkbd *ctx, uint8_t addr, uint8_t value) +{ + uint8_t buf[8]; + struct is31fl3743b_msg *msg = (void *)buf; + const int frame_len = sizeof(*msg) + 1; + + msg->cmd.read = 0; + msg->cmd.id = IS31FL3743B_CMD_ID; + msg->cmd.page = IS31FL3743B_PAGE_FUNC; + msg->addr = addr; + msg->payload[0] = value; + + return spi_transaction(SPI(ctx->cfg->spi), buf, frame_len, NULL, 0); +} + +static int is31fl3743b_reset(struct rgbkbd *ctx) +{ + return is31fl3743b_write(ctx, IS31FL3743B_REG_RSTN, 0xae); +} + +static int is31fl3743b_enable(struct rgbkbd *ctx, bool enable) +{ + uint8_t u8; + int rv; + + gpio_set_level(GPIO_RGBKBD_SDB_L, enable ? 1 : 0); + + rv = is31fl3743b_read(ctx, IS31FL3743B_REG_CONFIG, &u8); + if (rv) { + return rv; + } + + return is31fl3743b_write(ctx, IS31FL3743B_REG_CONFIG, + u8 | BIT(3) | (enable ? BIT(0) : 0)); +} + +static int is31fl3743b_set_color(struct rgbkbd *ctx, uint8_t offset, + struct rgb_s *color, uint8_t len) +{ + uint8_t buf[sizeof(struct is31fl3743b_msg) + IS31FL3743B_BUF_SIZE]; + struct is31fl3743b_msg *msg = (void *)buf; + const int frame_len = len * SIZE_OF_RGB + sizeof(*msg); + const int frame_offset = offset * SIZE_OF_RGB; + int i; + + msg->cmd.read = 0; + msg->cmd.id = IS31FL3743B_CMD_ID; + msg->cmd.page = IS31FL3743B_PAGE_PWM; + + if (frame_offset + frame_len > sizeof(buf)) { + return EC_ERROR_OVERFLOW; + } + + msg->addr = frame_offset + 1; /* Register addr base is 1. */ + for (i = 0; i < len; i++) { + msg->payload[i * SIZE_OF_RGB +0] = color[i].r; + msg->payload[i * SIZE_OF_RGB +1] = color[i].g; + msg->payload[i * SIZE_OF_RGB +2] = color[i].b; + } + + return spi_transaction(SPI(ctx->cfg->spi), buf, frame_len, NULL, 0); +} + +static int is31fl3743b_set_scale(struct rgbkbd *ctx, uint8_t offset, + uint8_t scale, uint8_t len) +{ + uint8_t buf[sizeof(struct is31fl3743b_msg) + IS31FL3743B_BUF_SIZE]; + struct is31fl3743b_msg *msg = (void *)buf; + const int frame_len = len * SIZE_OF_RGB + sizeof(*msg); + + msg->cmd.read = 0; + msg->cmd.id = IS31FL3743B_CMD_ID; + msg->cmd.page = IS31FL3743B_PAGE_SCALE; + + if (offset + frame_len > sizeof(buf)) { + return EC_ERROR_OVERFLOW; + } + + msg->addr = offset + 1; /* Address base is 1. */ + memset(msg->payload, scale, len * SIZE_OF_RGB); + + return spi_transaction(SPI(ctx->cfg->spi), buf, frame_len, NULL, 0); +} + +static int is31fl3743b_set_gcc(struct rgbkbd *ctx, uint8_t level) +{ + uint8_t buf[8]; + struct is31fl3743b_msg *msg = (void *)buf; + const int frame_len = sizeof(*msg) + 1; + + msg->cmd.read = 0; + msg->cmd.id = IS31FL3743B_CMD_ID; + msg->cmd.page = IS31FL3743B_PAGE_FUNC; + msg->addr = IS31FL3743B_REG_GCC; + msg->payload[0] = level; + + return spi_transaction(SPI(ctx->cfg->spi), buf, frame_len, NULL, 0); +} + +static int is31fl3743b_init(struct rgbkbd *ctx) +{ + int rv; + + rv = is31fl3743b_reset(ctx); + msleep(3); + rv |= is31fl3743b_enable(ctx, true); + if (rv) { + CPRINTS("Failed to enable or reset (%d)", rv); + return rv; + } + + if (IS_ENABLED(CONFIG_RGB_KEYBOARD_DEBUG)) { + uint8_t val; + rv = is31fl3743b_read(ctx, IS31FL3743B_REG_PD_PU, &val); + CPRINTS("PD/PU. val=0x%02x (rv=%d)", val, rv); + } + + return EC_SUCCESS; +} + +const struct rgbkbd_drv is31fl3743b_drv = { + .reset = is31fl3743b_reset, + .init = is31fl3743b_init, + .enable = is31fl3743b_enable, + .set_color = is31fl3743b_set_color, + .set_scale = is31fl3743b_set_scale, + .set_gcc = is31fl3743b_set_gcc, +}; diff --git a/include/config.h b/include/config.h index f7857f034b..576480f250 100644 --- a/include/config.h +++ b/include/config.h @@ -3114,6 +3114,7 @@ #undef CONFIG_LED_DRIVER_LP5562 /* LP5562, on I2C interface */ #undef CONFIG_LED_DRIVER_MP3385 /* MPS MP3385, on I2C */ #undef CONFIG_LED_DRIVER_OZ554 /* O2Micro OZ554, on I2C */ +#undef CONFIG_LED_DRIVER_IS31FL3743B /* Lumissil IS31FL3743B on SPI */ /* Offset in flash where little firmware will live. */ #undef CONFIG_LFW_OFFSET @@ -3635,6 +3636,29 @@ */ #undef CONFIG_KBLIGHT_ENABLE_PIN +/* + * RGB Keyboard + */ +#undef CONFIG_RGB_KEYBOARD + +/* + * Enable debug messages from a RGB keyboard task. + */ +#undef CONFIG_RGB_KEYBOARD_DEBUG + +/* + * Enable demo for RGB keyboard to run on reset. + * + * FLOW: In each iteration, a new color is placed in (0,0) and the rest of LEDs + * copy colors from adjacent LEDs. + * + * DOT: A red dot is placed on (0,0) and traverses the grid from top to bottom + * left to right. After the entire matrix is traversed, it's repeated with a + * new color. + */ +#undef CONFIG_RGBKBD_DEMO_FLOW +#undef CONFIG_RGBKBD_DEMO_DOT + /* Support Real-Time Clock (RTC) */ #undef CONFIG_RTC diff --git a/include/console_channel.inc b/include/console_channel.inc index 0a6d84509c..96691c21c3 100644 --- a/include/console_channel.inc +++ b/include/console_channel.inc @@ -62,6 +62,9 @@ CONSOLE_CHANNEL(CC_LOGOLED, "logoled") #ifdef HAS_TASK_LIGHTBAR CONSOLE_CHANNEL(CC_LIGHTBAR, "lightbar") #endif +#ifdef HAS_TASK_RGBKBD +CONSOLE_CHANNEL(CC_RGBKBD, "rgbkbd") +#endif /* Use CC_LPC for any host communication channels to x86, like LPC, eSPI, or IPC... diff --git a/include/ec_commands.h b/include/ec_commands.h index 99fbddd3f2..919215a085 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -7074,6 +7074,21 @@ struct ec_response_i2c_control { } cmd_response; } __ec_align_size1; +#define EC_CMD_RGB_KEYBOARD 0x13a + +enum rgbkbd_state { + /* RGB keyboard is reset and not initialized. */ + RGBKBD_STATE_RESET = 0, + /* RGB keyboard is initialized but not enabled. */ + RGBKBD_STATE_INITIALIZED, + /* RGB keyboard is enabled and ready to receive a command. */ + RGBKBD_STATE_ENABLED, + + /* Put no more entry below */ + RGBKBD_STATE_COUNT, +}; + + /*****************************************************************************/ /* The command range 0x200-0x2FF is reserved for Rotor. */ diff --git a/include/rgb_keyboard.h b/include/rgb_keyboard.h new file mode 100644 index 0000000000..793ee62f48 --- /dev/null +++ b/include/rgb_keyboard.h @@ -0,0 +1,121 @@ +/* Copyright 2022 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdbool.h> + +#include "common.h" +#include "ec_commands.h" + +/* Use this instead of '3' for readability where applicable. */ +#define SIZE_OF_RGB sizeof(struct rgb_s) + +enum rgbkbd_demo { + RGBKBD_DEMO_OFF = 0, + RGBKBD_DEMO_FLOW = 1, + RGBKBD_DEMO_DOT = 2, + RGBKBD_DEMO_COUNT +}; + +struct rgbkbd_cfg { + /* Driver for LED IC */ + const struct rgbkbd_drv * const drv; + /* SPI/I2C port (i.e. index of spi_devices[], i2c_ports[]) */ + union { + const uint8_t i2c; + const uint8_t spi; + }; + /* Grid size */ + const uint8_t col_len; + const uint8_t row_len; +}; + +struct rgbkbd { + /* Static configuration */ + const struct rgbkbd_cfg * const cfg; + /* Current state of the port */ + enum rgbkbd_state state; + /* Buffer containing color info for each dot. */ + struct rgb_s *buf; +}; + +struct rgbkbd_drv { + /* Reset charger chip. */ + int (*reset)(struct rgbkbd *ctx); + /* Initialize the charger. */ + int (*init)(struct rgbkbd *ctx); + /* Enable/disable the charger. Usually disabled means stand-by. */ + int (*enable)(struct rgbkbd *ctx, bool enable); + + /** + * Set the colors of multiple RGB-LEDs. + * + * @param ctx Context. + * @param offset Starting LED position. + * @param color Array of colors to set. Must be as long as <len>. + * @param len Length of <color> array. + * @return enum ec_error_list. + */ + int (*set_color)(struct rgbkbd *ctx, uint8_t offset, + struct rgb_s *color, uint8_t len); + /** + * Set the scale of multiple LEDs + * + * @param ctx Context. + * @param offset Starting LED position. + * @param scale Scale to be set. + * @param len Length of LEDs to be set. + * @return enum ec_error_list + */ + int (*set_scale)(struct rgbkbd *ctx, uint8_t offset, + uint8_t scale, uint8_t len); + /** + * Set global current control. + * + * @param level Global current control to set. + * @return enum ec_error_list. + */ + int (*set_gcc)(struct rgbkbd *ctx, uint8_t level); +}; + +/* + * The matrix consists of multiple grids: + * + * +=========-== Matrix =============+ + * | +--- Grid1 ---+ +--- Grid2 ---+ | ^ + * | | A C | | E | | | + * | | B | | | | rgbkbd_vsize + * | | D | | | | | + * | +-------------+ +-------------+ | v + * +=================================+ + * + * <-------- rgbkbd_hsize -------> + * + * Grids are assumed to be horizontally adjacent. That is, the matrix row size + * is fixed and the matrix column size is a multiple of the grid's column size. + * + * Grid coordinate format is (column, row). In the diagram above, + * A = (0, 0) + * B = (0, 1) + * C = (1, 0) + * + * In each grid, LEDs are also sequentially indexed. That is, + * A = 0 + * B = 1 + * C = rgbkbd_vsize + * E = 0 + * + * Matrix coordinate format is (x, y), where x is a horizontal position and y + * is a vertical position. + * E = (grid0_hsize, 0) + */ +extern struct rgbkbd rgbkbds[]; +extern const uint8_t rgbkbd_count; +extern const uint8_t rgbkbd_hsize; +extern const uint8_t rgbkbd_vsize; + +/* + * Called to power on or off the RGB keyboard module. + */ +__override_proto void board_enable_rgb_keyboard(bool enable); diff --git a/test/build.mk b/test/build.mk index 7051e98496..e57f6e5c8e 100644 --- a/test/build.mk +++ b/test/build.mk @@ -67,6 +67,7 @@ test-list-host += pingpong test-list-host += power_button test-list-host += printf test-list-host += queue +test-list-host += rgb_keyboard test-list-host += rsa test-list-host += rsa3 test-list-host += rtc @@ -192,6 +193,7 @@ motion_lid-y=motion_lid.o motion_sense_fifo-y=motion_sense_fifo.o online_calibration-y=online_calibration.o online_calibration_spoof-y=online_calibration_spoof.o gyro_cal_init_for_test.o +rgb_keyboard-y=rgb_keyboard.o kasa-y=kasa.o mpu-y=mpu.o mutex-y=mutex.o diff --git a/test/rgb_keyboard.c b/test/rgb_keyboard.c new file mode 100644 index 0000000000..2c36bf9030 --- /dev/null +++ b/test/rgb_keyboard.c @@ -0,0 +1,348 @@ +/* Copyright 2022 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Tests for RGB keyboard. + */ +#include <stdio.h> + +#include "common.h" +#include "console.h" +#include "rgb_keyboard.h" +#include "task.h" +#include "test_util.h" +#include "timer.h" +#include "util.h" + +#define RGB_GRID0_COL 11 +#define RGB_GRID0_ROW 6 +#define RGB_GRID1_COL 11 +#define RGB_GRID1_ROW 6 +#define SPI_RGB0_DEVICE_ID 0 +#define SPI_RGB1_DEVICE_ID 1 + +static struct rgb_s grid0[RGB_GRID0_COL * RGB_GRID0_ROW]; +static struct rgb_s grid1[RGB_GRID1_COL * RGB_GRID1_ROW]; + +const struct rgbkbd_drv test_drv; + +struct rgbkbd rgbkbds[] = { + [0] = { + .cfg = &(const struct rgbkbd_cfg) { + .drv = &test_drv, + .spi = SPI_RGB0_DEVICE_ID, + .col_len = RGB_GRID0_COL, + .row_len = RGB_GRID0_ROW, + }, + .buf = grid0, + }, + [1] = { + .cfg = &(const struct rgbkbd_cfg) { + .drv = &test_drv, + .spi = SPI_RGB1_DEVICE_ID, + .col_len = RGB_GRID1_COL, + .row_len = RGB_GRID1_ROW, + }, + .buf = grid1, + }, +}; +const uint8_t rgbkbd_count = ARRAY_SIZE(rgbkbds); +const uint8_t rgbkbd_hsize = RGB_GRID0_COL + RGB_GRID1_COL; +const uint8_t rgbkbd_vsize = RGB_GRID0_ROW; + +static struct rgbkbd_mock { + uint32_t count_drv_reset; + uint32_t count_drv_init; + uint32_t count_drv_enable; + uint32_t count_drv_set_color; + uint32_t count_drv_set_scale; + uint32_t count_drv_set_gcc; + uint32_t gcc_level; +} mock_state; + +void before_test(void) +{ + memset(&mock_state, 0, sizeof(mock_state)); +} + +static int test_drv_reset(struct rgbkbd *ctx) +{ + mock_state.count_drv_reset++; + return EC_SUCCESS; +} + +static int test_drv_init(struct rgbkbd *ctx) +{ + mock_state.count_drv_init++; + return EC_SUCCESS; +} + +static int test_drv_enable(struct rgbkbd *ctx, bool enable) +{ + mock_state.count_drv_enable++; + return EC_SUCCESS; +} + +static int test_drv_set_color(struct rgbkbd *ctx, uint8_t offset, + struct rgb_s *color, uint8_t len) +{ + mock_state.count_drv_set_color++; + return EC_SUCCESS; +} + + +static int test_drv_set_scale(struct rgbkbd *ctx, uint8_t offset, + uint8_t scale, uint8_t len) +{ + mock_state.count_drv_set_scale++; + return EC_SUCCESS; +} + +static int test_drv_set_gcc(struct rgbkbd *ctx, uint8_t level) +{ + mock_state.count_drv_set_gcc++; + mock_state.gcc_level = level; + return EC_SUCCESS; +} + +const struct rgbkbd_drv test_drv = { + .reset = test_drv_reset, + .init = test_drv_init, + .enable = test_drv_enable, + .set_color = test_drv_set_color, + .set_scale = test_drv_set_scale, + .set_gcc = test_drv_set_gcc, +}; + +static int test_rgbkbd_startup(void) +{ + struct rgbkbd *ctx; + struct rgb_s color; + int g, x, y, c, r; + + /* Let RGBKBD task run. */ + task_wait_event(-1); + + zassert_equal(mock_state.count_drv_init, rgbkbd_count, + "init() called"); + zassert_equal(mock_state.count_drv_set_gcc, rgbkbd_count, + "set_gcc() called"); + zassert_equal(mock_state.count_drv_set_scale, rgbkbd_count, + "set_scale() called"); + + /* Check 'DOT' demo. */ + for (x = 0; x < rgbkbd_hsize; x++) { + g = x / rgbkbds[0].cfg->col_len; + c = x % rgbkbds[0].cfg->col_len; + ctx = &rgbkbds[g]; + for (y = 0; y < ctx->cfg->row_len; y++) { + r = y; + color = ctx->buf[ctx->cfg->row_len * c + r]; + zassert_equal(color.r, 0, "R = 0"); + zassert_equal(color.g, 0, "G = 0"); + zassert_equal(color.b, 0, "B = 0"); + + r++; + if (r >= ctx->cfg->row_len) { + r = 0; + c++; + if (c >= rgbkbds[0].cfg->col_len) { + task_wait_event(-1); + break; + } + } + color = ctx->buf[ctx->cfg->row_len * c + r]; + zassert_equal(color.r, 0x80, "R = 0x80"); + zassert_equal(color.g, 0, "G = 0"); + zassert_equal(color.b, 0, "B = 0"); + + task_wait_event(-1); + } + } + + return EC_SUCCESS; +} + +int cc_rgbk(int argc, char **argv); +extern enum rgbkbd_demo demo; + +static int test_rgbkbd_console_command(void) +{ + struct rgbkbd *ctx; + int argc; + char buf[8]; + int i, x, y, r, c; + uint8_t offset; + char *argv_demo[] = {"rgbk", "demo", "0"}; + char *argv_gcc[] = {"rgbk", "100"}; + char *argv_color[] = {"rgbk", buf, "1", "2", "3"}; + char *argv_all[] = {"rgbk", "all", "1", "2", "3"}; + + /* Test 'rgbk demo 0'. */ + before_test(); + argc = ARRAY_SIZE(argv_demo); + zassert_equal(demo, 2, "demo == 2"); + zassert_equal(cc_rgbk(argc, argv_demo), EC_SUCCESS, "rgbk demo 0"); + zassert_equal(demo, 0, "demo == 0"); + + /* Test 'rgbk 100'. */ + before_test(); + argc = ARRAY_SIZE(argv_gcc); + zassert_equal(cc_rgbk(argc, argv_gcc), EC_SUCCESS, "rgbk 100"); + zassert_equal(mock_state.count_drv_set_gcc, rgbkbd_count, + "set_gcc() called"); + zassert_equal(mock_state.gcc_level, 100, "gcc == 100"); + + /* Test 'rgbk 1,1 1 2 3'. */ + before_test(); + ctx = &rgbkbds[0]; + x = 1; + y = 1; + offset = rgbkbd_vsize * x + y; + sprintf(buf, "%d,%d", x, y); + argc = ARRAY_SIZE(argv_color); + zassert_equal(cc_rgbk(argc, argv_color), EC_SUCCESS, + "rgbk %s 1 2 3", buf); + zassert_equal(ctx->buf[offset].r, 1, "R = 1"); + zassert_equal(ctx->buf[offset].g, 2, "G = 2"); + zassert_equal(ctx->buf[offset].b, 3, "B = 3"); + + /* Test 'rgbk 1,-1 1 2 3'. */ + before_test(); + ctx = &rgbkbds[0]; + x = 1; + y = -1; + sprintf(buf, "%d,%d", x, y); + argc = ARRAY_SIZE(argv_color); + zassert_equal(cc_rgbk(argc, argv_color), EC_SUCCESS, + "rgbk %s 1 2 3", buf); + for (r = 0; r < rgbkbd_vsize; r++) { + offset = rgbkbd_vsize * x + r; + zassert_equal(ctx->buf[offset].r, 1, "R = 1"); + zassert_equal(ctx->buf[offset].g, 2, "G = 2"); + zassert_equal(ctx->buf[offset].b, 3, "B = 3"); + } + + /* Test 'rgbk -1,1 1 2 3'. */ + before_test(); + x = -1; + y = 1; + sprintf(buf, "%d,%d", x, y); + argc = ARRAY_SIZE(argv_color); + zassert_equal(cc_rgbk(argc, argv_color), EC_SUCCESS, + "rgbk %s 1 2 3", buf); + for (c = 0; c < rgbkbd_hsize; c++) { + ctx = &rgbkbds[c / rgbkbds[0].cfg->col_len]; + offset = rgbkbd_vsize * (c % ctx->cfg->col_len) + y; + zassert_equal(ctx->buf[offset].r, 1, "R = 1"); + zassert_equal(ctx->buf[offset].g, 2, "G = 2"); + zassert_equal(ctx->buf[offset].b, 3, "B = 3"); + } + + /* Test 'rgbk all 1 2 3'. */ + before_test(); + argc = ARRAY_SIZE(argv_all); + zassert_equal(cc_rgbk(argc, argv_all), EC_SUCCESS, "rgbk all 1 2 3"); + for (i = 0; i < rgbkbd_count; i++) { + ctx = &rgbkbds[i]; + for (c = 0; c < ctx->cfg->col_len; c++) { + for (r = 0; r < ctx->cfg->row_len; r++) { + offset = rgbkbd_vsize * c + r; + zassert_equal(ctx->buf[offset].r, 1, "R = 1"); + zassert_equal(ctx->buf[offset].g, 2, "G = 2"); + zassert_equal(ctx->buf[offset].b, 3, "B = 3"); + } + } + } + + return EC_SUCCESS; +} + +struct rgb_s rotate_color(struct rgb_s color, int step); +uint8_t get_grid_size(const struct rgbkbd *ctx); + +static int test_rgbkbd_rotate_color(void) +{ + struct rgb_s color = {}; + const int step = 32; + int r, g, b; + + for (b = 0; b < 0x100 / step; b++) { + zassert_equal(color.r, 0, "R = 0"); + zassert_equal(color.g, 0, "G = 0"); + zassert_equal(color.b, b * step, "B += 32"); + for (g = 0; g < 0x100 / step; g++) { + zassert_equal(color.r, 0, "R = 0"); + zassert_equal(color.g, g * step, "G += 32"); + zassert_equal(color.b, b * step, "B = B"); + for (r = 0; r < 0x100 / step; r++) { + zassert_equal(color.r, r * step, "R += 32"); + zassert_equal(color.g, g * step, "G = G"); + zassert_equal(color.b, b * step, "B = B"); + color = rotate_color(color, step); + } + } + } + zassert_equal(color.r, 0, "R = 0"); + zassert_equal(color.g, 0, "G = 0"); + zassert_equal(color.b, 0, "B = 0"); + + return EC_SUCCESS; +} + +static int test_rgbkbd_demo_flow(void) +{ + struct rgb_s copy[ARRAY_SIZE(rgbkbds)][RGB_GRID0_COL * RGB_GRID0_ROW]; + char *argv_demo[] = {"rgbk", "demo", "1"}; + struct rgb_s *p; + int argc; + struct rgbkbd *ctx; + struct rgb_s color = {}; + const int step = 32; + uint8_t len; + int i, j, g; + + argc = ARRAY_SIZE(argv_demo); + zassert_equal(cc_rgbk(argc, argv_demo), EC_SUCCESS, "rgbk demo flow"); + + for (j = 0; j < 0x100 / step; j++) { + /* Take a snapshot. */ + memcpy(©[0], rgbkbds[0].buf, sizeof(copy[0])); + memcpy(©[1], rgbkbds[1].buf, sizeof(copy[1])); + + /* Let demo run one iteration. */ + task_wait_event(-1); + + /* Compare with the snapshot. */ + for (g = rgbkbd_count - 1; g >= 0; g--) { + ctx = &rgbkbds[g]; + p = copy[g]; + len = get_grid_size(ctx); + for (i = len - 1; i > 0; i--) { + zassert_equal(ctx->buf[i].r, p[i - 1].r, + "i <- i-1"); + } + if (g > 0) { + len = get_grid_size(&rgbkbds[g - 1]); + p = copy[g - 1]; + zassert_equal(ctx->buf[0].r, p[len - 1].r, + "grid1[0] <- grid0[last]"); + } + } + + /* After one run, a new color will be injected to (0,0). */ + color = rotate_color(color, step); + zassert_equal(ctx->buf[0].r, color.r, "(0,0) <- new color"); + } + + return EC_SUCCESS; +} + +void run_test(int argc, char **argv) +{ + RUN_TEST(test_rgbkbd_startup); + RUN_TEST(test_rgbkbd_console_command); + RUN_TEST(test_rgbkbd_rotate_color); + RUN_TEST(test_rgbkbd_demo_flow); + test_print_result(); +} diff --git a/test/rgb_keyboard.tasklist b/test/rgb_keyboard.tasklist new file mode 100644 index 0000000000..fb85751609 --- /dev/null +++ b/test/rgb_keyboard.tasklist @@ -0,0 +1,10 @@ +/* Copyright 2022 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/** + * See CONFIG_TASK_LIST in config.h for details. + */ +#define CONFIG_TEST_TASK_LIST \ + TASK_TEST(RGBKBD, rgbkbd_task, NULL, 2048) diff --git a/test/test_config.h b/test/test_config.h index 7da4d817d6..095011f2e4 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -137,6 +137,11 @@ #define CONFIG_MKBP_USE_GPIO #endif +#ifdef TEST_RGB_KEYBOARD +#define CONFIG_RGB_KEYBOARD +#define CONFIG_RGBKBD_DEMO_DOT +#endif + #ifdef TEST_STILLNESS_DETECTOR #define CONFIG_MKBP_EVENT #define CONFIG_MKBP_USE_GPIO diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 3a83b4a440..65534d3572 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -273,6 +273,8 @@ zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_IOEX_TCA64XXA "${PLATFORM_EC}/driver/ioexpander/tca64xxa.c") zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_KEYBOARD "${PLATFORM_EC}/common/keyboard_scan.c") +zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_RGB_KEYBOARD + "${PLATFORM_EC}/common/rgb_keyboard.c") zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_KEYBOARD_PROTOCOL_8042 "${PLATFORM_EC}/common/keyboard_8042.c" "${PLATFORM_EC}/common/keyboard_8042_sharedlib.c") diff --git a/zephyr/Kconfig.keyboard b/zephyr/Kconfig.keyboard index 12e1817de0..d2d7efc8af 100644 --- a/zephyr/Kconfig.keyboard +++ b/zephyr/Kconfig.keyboard @@ -198,3 +198,52 @@ config PLATFORM_EC_KBLIGHT_ENABLE_PIN disable pin. This pin must be defined as GPIO_EN_KEYBOARD_BACKLIGHT. endif # PLATFORM_EC_KEYBOARD + + +menuconfig PLATFORM_EC_RGB_KEYBOARD + bool "RGB keyboard support" + select HAS_TASK_RGBKBD + help + Enable RGB keyboard backlight control. This consists of a RGB matrix + control task. Note this feature does not include key scanning. + +if PLATFORM_EC_RGB_KEYBOARD + +config PLATFORM_EC_RGB_KEYBOARD_DEBUG + bool "RGB Keyboard Debug" + default n + help + Enable debug messages from a RGB keyboard task. + +choice PLATFORM_EC_RGBKBD_DEMO + prompt "RGB Keyboard demo to run on reset" + optional + help + Enable demo of RGB keyboard to run on reset. + +config PLATFORM_EC_RGBKBD_DEMO_FLOW + bool "Flow" + help + In each iteration, a new color is placed in (0,0) and the rest of + LEDs copy colors from adjacent LEDs. + +config PLATFORM_EC_RGBKBD_DEMO_DOT + bool "Dot" + help + A red dot is placed on (0,0) and traverses the grid from top to buttom + left to right. After the entire matrix is traversed, it's repeated with + a new color. + +endchoice # PLATFORM_EC_RGBKBD_DEMO + +config PLATFORM_EC_LED_DRIVER_IS31FL3743B + bool "Driver for IS31FL3743B LED controller" + help + Enable driver for IS31FL3743B LED controller. + +config PLATFORM_EC_LED_DRIVER_AW20198 + bool "Driver for AW20198 LED controller" + help + Enable driver for AW20198 LED controller. + +endif # PLATFORM_EC_RGB_KEYBOARD |