i2c/ww_ring: define display pattern programs

Add compiled lp55231 code snippets to allow display certain patterns
when booting the device with the recovery button pressed.

As soon as the press is detected, the low intensify solid white
pattern is enabled. Holding recovery button long enough causes the
device transition between the wipeout requested and recovery requested
states, with the appropriate changes in the displayed pattern.

The patch also includes the source code for the LED controller as well
as instructions on how to compile and modify the code to result in
different colors, intensities, blink periods and duty cycles.

BRANCH=storm
BUG=chrome-os-partner:36059
TEST=reboot an SP5 device with the LED ring attached, keep the
     recovery button pressed, observe the changes in the LED display
     pattern while the device progresses through the boot sequence.

Change-Id: Ic7d45fc7c313b6d21119d4ae6adaeb4f46f7d181
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 0fd6a5c0067d705197816629f41640a931d2f7cd
Original-Change-Id: Ib5cc5188c2eeedbba128101bf4092a0b9a74e155
Original-Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/260670
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/9870
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Vadim Bendebury 2015-03-17 15:15:42 -07:00 committed by Patrick Georgi
parent 4e0de324eb
commit 5fa5f99d47
4 changed files with 395 additions and 58 deletions

View File

@ -1 +1,2 @@
verstage-$(CONFIG_DRIVERS_I2C_WW_RING) += ww_ring.c
verstage-$(CONFIG_DRIVERS_I2C_WW_RING) += ww_ring_programs.c

View File

@ -31,10 +31,7 @@
#include <device/i2c.h>
#include <string.h>
#include "drivers/i2c/ww_ring/ww_ring.h"
/* Number of lp55321 controllers on the ring */
#define WW_RING_NUM_LED_CONTROLLERS 2
#include "drivers/i2c/ww_ring/ww_ring_programs.h"
/* I2c address of the first of the controllers, the rest are contiguous. */
#define WW_RING_BASE_ADDR 0x32
@ -84,9 +81,6 @@
#define LP55231_PROG_PAGES 6
#define LP55231_MAX_PROG_SIZE (LP55231_PROG_PAGE_SIZE * LP55231_PROG_PAGES)
/* There are threee independent engines/cores in the controller. */
#define LP55231_NUM_OF_ENGINES 3
/*
* Structure to cache data relevant to accessing one controller. I2c interface
* to use, device address on the i2c bus and a data buffer for write
@ -99,55 +93,8 @@ typedef struct {
uint8_t data_buffer[LP55231_PROG_PAGE_SIZE + 1];
} TiLp55231;
/*
* Structure to describe an lp55231 program: pointer to the text of the
* program, its size and load address (load addr + size sould not exceed
* LP55231_MAX_PROG_SIZE), and start addresses for all of the three
* engines.
*/
typedef struct {
const uint8_t *program_text;
uint8_t program_size;
uint8_t load_addr;
uint8_t engine_start_addr[LP55231_NUM_OF_ENGINES];
} TiLp55231Program;
/* A structure to bind controller programs to a vboot state. */
typedef struct {
enum display_pattern led_pattern;
const TiLp55231Program * programs[WW_RING_NUM_LED_CONTROLLERS];
} WwRingStateProg;
static void ww_ring_init(unsigned i2c_bus);
/****************************************************************/
/* LED ring program definitions for different vboot states. */
static const uint8_t blink_program_text[] = {
0x40, 0x40, 0x9D, 0x04, 0x40, 0x40, 0x7E,
0x00, 0x9D, 0x07, 0x40, 0x00, 0x9D, 0x04,
0x40, 0x00, 0x7E, 0x00, 0xA0, 0x00, 0x00,
0x00 };
static const TiLp55231Program led_blink_program = {
blink_program_text,
sizeof(blink_program_text),
0,
{0,
sizeof(blink_program_text) - 2,
sizeof(blink_program_text) - 2}
};
static const WwRingStateProg state_programs[] = {
/*
* for test purposes the blank screen program is set to blinking, will
* be changed soon.
*/
{WWR_ALL_OFF, {&led_blink_program, &led_blink_program} },
};
/* */
/****************************************************************/
/* Controller descriptors. */
static TiLp55231 lp55231s[WW_RING_NUM_LED_CONTROLLERS];
@ -389,23 +336,24 @@ static int ledc_init_validate(TiLp55231 *ledc)
*/
int ww_ring_display_pattern(unsigned i2c_bus, enum display_pattern pattern)
{
int i;
static int initted;
const WwRingStateProg *wwr_prog;
if (!initted) {
ww_ring_init(i2c_bus);
initted = 1;
}
for (i = 0; i < ARRAY_SIZE(state_programs); i++)
if (state_programs[i].led_pattern == pattern) {
/* Last entry does not have any actual programs defined. */
for (wwr_prog = wwr_state_programs; wwr_prog->programs[0]; wwr_prog++)
if (wwr_prog->led_pattern == pattern) {
int j;
for (j = 0; j < WW_RING_NUM_LED_CONTROLLERS; j++) {
if (!lp55231s[j].dev_addr)
continue;
ledc_run_program(lp55231s + j,
state_programs[i].programs[j]);
wwr_prog->programs[j]);
}
return 0;
}

View File

@ -0,0 +1,326 @@
/*
* Copyright (C) 2015 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* This is a driver for the Whirlwind LED ring, which is equipped with two LED
* microcontrollers TI LP55231 (http://www.ti.com/product/lp55231), each of
* them driving three multicolor LEDs.
*
* The only connection between the ring and the main board is an i2c bus.
*
* This driver imitates a depthcharge display device. On initialization the
* driver sets up the controllers to prepare them to accept programs to run.
*
* When a certain vboot state needs to be indicated, the program for that
* state is loaded into the controllers, resulting in the state appropriate
* LED behavior.
*/
#include "drivers/i2c/ww_ring/ww_ring_programs.h"
/****************************************************************
* LED ring program definitions for different patterns.
*
* Comments below are real lp55231 source code, they are compiled using
* lasm.exe, the TI tool available from their Web site (search for lp55231)
* and running only on Windows :P.
*
* Different hex dumps are results of tweaking the source code parameters to
* achieve desirable LED ring behavior. It is possible to use just one code
* dump and replace certain values in the body to achieve different behaviour
* with the same basic dump, but keeping track of location to tweak with every
* code change would be quite tedious.
*/
/*
* Solid LED display, the arguments of the set_pwm commands set intensity and
* color of the display:
row_red: dw 0000000001001001b
row_green: dw 0000000010010010b
row_blue: dw 0000000100100100b
.segment program1
mux_map_addr row_red
set_pwm 1
end
.segment program2
mux_map_addr row_green
set_pwm 1
end
.segment program3
mux_map_addr row_blue
set_pwm 1
end
*/
/* RGB set to 000000, resulting in all LEDs off. */
static const uint8_t solid_000000_text[] = {
0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x9F, 0x80,
0x40, 0x00, 0xC0, 0x00, 0x9F, 0x81, 0x40, 0x00,
0xC0, 0x00, 0x9F, 0x82, 0x40, 0x00, 0xC0, 0x00
};
/* RGB set to 010101, resulting in a bleak greyish color. */
static const uint8_t solid_010101_text[] = {
0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x9F, 0x80,
0x40, 0x01, 0xC0, 0x00, 0x9F, 0x81, 0x40, 0x01,
0xC0, 0x00, 0x9F, 0x82, 0x40, 0x01, 0xC0, 0x00
};
static const TiLp55231Program solid_010101_program = {
solid_010101_text,
sizeof(solid_010101_text),
0,
{ 3, 6, 9 }
};
static const TiLp55231Program solid_000000_program = {
solid_000000_text,
sizeof(solid_000000_text),
0,
{ 3, 6, 9 }
};
/*
* Blinking patterns are much tricker then solid ones.
*
* The three internal engines seem to be competing for resources and get out
* of sync in seconds if left running asynchronously.
*
* When there are two separate controllers, with three engine each, they all
* run away from each other in no time, resulting in some completely chaotic
* LED behavior.
*
* To keep the ring in check internal and external triggers are used which
* makes programs for controller1 and controller2 sligtly different.
*
* The first controller is loaded and started first, but it sits waiting for
* the trigger from the second controller to actually start the cycle.
*
* In the middle of the cycle the first controller sends a sync back to the
* second one. Both controllers' engine1 also synchs up their respective
* engines 2 and 3.
*
* The maximum timer duration of lp55231 is .48 seconds. To achieve longer
* blinking intervals the loops delays are deployed.
*
* The granularity is set at .1 second (see commands 'wait 0.1' in the code,
* and then the loop counters can be set up to 63 (registers rb and rc), which
* allows to generate intervals up to 6.3 seconds in .1 second increments.
*/
/* blink_solid1.src
row_red: dw 0000000001001001b
row_green: dw 0000000010010010b
row_blue: dw 0000000100100100b
.segment program1
ld ra, 255 ; red intensity
ld rb, 2 ; up time 200 ms
ld rc, 2 ; down time 200 ms
mux_map_addr row_red
loop1: trigger w{e} ; wait for external trigger from 2nd controller
trigger s{2|3}
set_pwm ra
common1:
wait 0.1
branch rb, common1
trigger s{2|3|e}
set_pwm 0
wait1:
wait 0.1
branch rc, wait1
branch 0, loop1
.segment program2
mux_map_addr row_green
ld ra, 255 ; green intensity
loop2: trigger w{1}
set_pwm ra
common2:
; engine 2 and 3 intervals are controlled by sync with engine 1
wait 0.1
branch 1, common2
trigger w{1}
set_pwm 0
wait2:
wait 0.1
branch 1, wait2
branch 0, loop2
.segment program3
ld ra, 0 ; blue intensity
loop3: trigger w{1}
set_pwm ra
common3:
wait 0.1
branch 1, common3
trigger w{1}
set_pwm 0
wait3:
wait 0.1
branch 1, wait3
branch 0, loop3
*/
/* blink_solid2.src
row_red: dw 0000000001001001b
row_green: dw 0000000010010010b
row_blue: dw 0000000100100100b
.segment program1
ld ra, 255 ; red intensity
ld rb, 2 ; up time
ld rc, 2 ; down time
mux_map_addr row_red
loop1: trigger s{2|3|e} ; send trigger to own engines and the first controller
set_pwm ra
common1:
wait 0.1
branch rb, common1
trigger w{e}
trigger s{2|3}
set_pwm 0
wait1:
wait 0.1
branch rc, wait1
branch 0, loop1
.segment program2
mux_map_addr row_green
ld ra, 255
loop2: trigger w{1}
set_pwm ra
common2:
wait 0.1
branch 1, common2
trigger w{1}
set_pwm 0
wait2:
wait 0.1
branch 1, wait2
branch 0, loop2
.segment program3
mux_map_addr row_blue
ld ra, 0
loop3: trigger w{1}
set_pwm ra
common3:
wait 0.1
branch 1, common3
trigger w{1}
set_pwm 0
wait3:
wait 0.1
branch 1, wait3
branch 0, loop3
*/
static const uint8_t blink_wipeout1_text[] = {
0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x90, 0xff,
0x94, 0x02, 0x98, 0x02, 0x9f, 0x80, 0xf0, 0x00,
0xe0, 0x0c, 0x84, 0x60, 0x4c, 0x00, 0x86, 0x1d,
0xe0, 0x4c, 0x40, 0x00, 0x4c, 0x00, 0x86, 0x2e,
0xa0, 0x04, 0x9f, 0x81, 0x90, 0xff, 0xe0, 0x80,
0x84, 0x60, 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80,
0x40, 0x00, 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02,
0x9f, 0x82, 0x90, 0x00, 0xe0, 0x80, 0x84, 0x60,
0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80, 0x40, 0x00,
0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02, 0x00, 0x00,
};
static const uint8_t blink_wipeout2_text[] = {
0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x90, 0xff,
0x94, 0x02, 0x98, 0x02, 0x9f, 0x80, 0xe0, 0x4c,
0x84, 0x60, 0x4c, 0x00, 0x86, 0x19, 0xf0, 0x00,
0xe0, 0x0c, 0x40, 0x00, 0x4c, 0x00, 0x86, 0x2e,
0xa0, 0x04, 0x9f, 0x81, 0x90, 0xff, 0xe0, 0x80,
0x84, 0x60, 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80,
0x40, 0x00, 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02,
0x9f, 0x82, 0x90, 0x00, 0xe0, 0x80, 0x84, 0x60,
0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80, 0x40, 0x00,
0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02, 0x00, 0x00,
};
static const TiLp55231Program blink_wipeout1_program = {
blink_wipeout1_text,
sizeof(blink_wipeout1_text),
0,
{ 3, 17, 28, }
};
static const TiLp55231Program blink_wipeout2_program = {
blink_wipeout2_text,
sizeof(blink_wipeout2_text),
0,
{ 3, 17, 28, }
};
static const uint8_t blink_recovery1_text[] = {
0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x90, 0xff,
0x94, 0x02, 0x98, 0x02, 0x9f, 0x80, 0xf0, 0x00,
0xe0, 0x0c, 0x84, 0x60, 0x4c, 0x00, 0x86, 0x1d,
0xe0, 0x4c, 0x40, 0x00, 0x4c, 0x00, 0x86, 0x2e,
0xa0, 0x04, 0x9f, 0x81, 0x90, 0x3d, 0xe0, 0x80,
0x84, 0x60, 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80,
0x40, 0x00, 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02,
0x90, 0x00, 0xe0, 0x80, 0x84, 0x60, 0x4c, 0x00,
0xa0, 0x83, 0xe0, 0x80, 0x40, 0x00, 0x4c, 0x00,
0xa0, 0x87, 0xa0, 0x01, 0x00, 0x00, 0x00,
};
static const TiLp55231Program blink_recovery1_program = {
blink_recovery1_text,
sizeof(blink_recovery1_text),
0,
{ 3, 17, 28, }
};
static const uint8_t blink_recovery2_text[] = {
0x00, 0x49, 0x00, 0x92, 0x01, 0x24, 0x90, 0xff,
0x94, 0x02, 0x98, 0x02, 0x9f, 0x80, 0xe0, 0x4c,
0x84, 0x60, 0x4c, 0x00, 0x86, 0x19, 0xf0, 0x00,
0xe0, 0x0c, 0x40, 0x00, 0x4c, 0x00, 0x86, 0x2e,
0xa0, 0x04, 0x9f, 0x81, 0x90, 0x3d, 0xe0, 0x80,
0x84, 0x60, 0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80,
0x40, 0x00, 0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02,
0x9f, 0x82, 0x90, 0x00, 0xe0, 0x80, 0x84, 0x60,
0x4c, 0x00, 0xa0, 0x84, 0xe0, 0x80, 0x40, 0x00,
0x4c, 0x00, 0xa0, 0x88, 0xa0, 0x02, 0x00, 0x00,
0x00,
};
static const TiLp55231Program blink_recovery2_program = {
blink_recovery2_text,
sizeof(blink_recovery2_text),
0,
{ 3, 17, 28, }
};
const WwRingStateProg wwr_state_programs[] = {
/*
* for test purposes the blank screen program is set to blinking, will
* be changed soon.
*/
{WWR_ALL_OFF, {&solid_000000_program, &solid_000000_program} },
{WWR_RECOVERY_PUSHED, {&solid_010101_program, &solid_010101_program} },
{WWR_WIPEOUT_REQUEST, {&blink_wipeout1_program,
&blink_wipeout2_program} },
{WWR_RECOVERY_REQUEST, {&blink_recovery1_program,
&blink_recovery2_program} },
{}, /* Empty record to mark the end of the table. */
};

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2015 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* This is a driver for the Whirlwind LED ring, which is equipped with two LED
* microcontrollers TI LP55231 (http://www.ti.com/product/lp55231), each of
* them driving three multicolor LEDs.
*
* The only connection between the ring and the main board is an i2c bus.
*
* This driver imitates a depthcharge display device. On initialization the
* driver sets up the controllers to prepare them to accept programs to run.
*
* When a certain vboot state needs to be indicated, the program for that
* state is loaded into the controllers, resulting in the state appropriate
* LED behavior.
*/
#ifndef __THIRD_PARTY_COREBOOT_SRC_DRIVERS_I2C_WW_RING_WW_RING_PROGRAMS_H__
#define __THIRD_PARTY_COREBOOT_SRC_DRIVERS_I2C_WW_RING_WW_RING_PROGRAMS_H__
#include <stdint.h>
#include "drivers/i2c/ww_ring/ww_ring.h"
/* There are threee independent engines/cores in the controller. */
#define LP55231_NUM_OF_ENGINES 3
/* Number of lp55321 controllers on the ring */
#define WW_RING_NUM_LED_CONTROLLERS 2
/*
* Structure to describe an lp55231 program: pointer to the text of the
* program, its size and load address (load addr + size sould not exceed
* LP55231_MAX_PROG_SIZE), and start addresses for all of the three
* engines.
*/
typedef struct {
const uint8_t *program_text;
uint8_t program_size;
uint8_t load_addr;
uint8_t engine_start_addr[LP55231_NUM_OF_ENGINES];
} TiLp55231Program;
/* A structure to bind controller programs to a vboot state. */
typedef struct {
enum display_pattern led_pattern;
const TiLp55231Program *programs[WW_RING_NUM_LED_CONTROLLERS];
} WwRingStateProg;
extern const WwRingStateProg wwr_state_programs[];
#endif