| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| /* |
| * 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 0000000000000001b |
| row_green: dw 0000000000000010b |
| row_blue: dw 0000000000000100b |
| |
| .segment program1 |
| mux_map_addr row_red |
| set_pwm 0 |
| end |
| |
| .segment program2 |
| mux_map_addr row_green |
| set_pwm 0 |
| end |
| |
| .segment program3 |
| mux_map_addr row_blue |
| set_pwm 0 |
| end |
| */ |
| |
| /* RGB set to 000000, resulting in all LEDs off. */ |
| static const uint8_t solid_000000_text[] = { |
| 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x9F, 0x80, |
| 0x40, 0, 0xC0, 0x00, 0x9F, 0x81, 0x40, 0, |
| 0xC0, 0x00, 0x9F, 0x82, 0x40, 0, 0xC0, 0x00 |
| }; |
| |
| /* Rgb set to 128, resulting in a brightish white color. */ |
| static const uint8_t solid_808080_text[] = { |
| 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x9F, 0x80, |
| 0x40, 128, 0xC0, 0x00, 0x9F, 0x81, 0x40, 128, |
| 0xC0, 0x00, 0x9F, 0x82, 0x40, 128, 0xC0, 0x00 |
| }; |
| |
| static const TiLp55231Program solid_808080_program = { |
| solid_808080_text, |
| sizeof(solid_808080_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 trickier 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 solid patterns are deployed with instantaneous color intensity |
| * changes, all three LEDs can be controlled by one engine in sequential |
| * accesses. But the controllers still need to be synchronized. |
| * |
| * The maximum timer duration of lp55231 is .48 seconds. To achieve longer |
| * blinking intervals the loops delays are deployed. Only the first controller |
| * intervals need to be changed, as the second one is in lockstep with the |
| * first. |
| * |
| * The time 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 0000000000000001b |
| row_green: dw 0000000000000010b |
| row_blue: dw 0000000000000100b |
| |
| .segment program1 |
| ld ra, 2 # LED on duration |
| ld rb, 2 # LED off duration |
| mux_map_addr row_red |
| loop: |
| ld rc, 1 ; red intensity |
| set_pwm rc |
| mux_map_addr row_green |
| ld rc, 50 ; green intensity |
| set_pwm rc |
| mux_map_addr row_blue |
| ld rc, 155 ; blue intensity |
| set_pwm rc |
| wait1: |
| wait 0.1 |
| branch ra, wait1 |
| set_pwm 0 |
| mux_map_addr row_green |
| set_pwm 0 |
| mux_map_addr row_red |
| set_pwm 0 |
| wait2: |
| wait 0.1 |
| branch rb, wait2 |
| branch 0, loop |
| .segment program2 |
| end |
| .segment program3 |
| end |
| */ |
| static const uint8_t blink_wipeout1_text[] = { |
| 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x90, 0x02, |
| 0x94, 0x02, 0x9f, 0x80, 0x98, 1, 0x84, 0x62, |
| 0x9f, 0x81, 0x98, 50, 0x84, 0x62, 0x9f, 0x82, |
| 0x98, 155, 0x84, 0x62, 0x4c, 0x00, 0x86, 0x2c, |
| 0x40, 0x00, 0x9f, 0x81, 0x40, 0x00, 0x9f, 0x80, |
| 0x40, 0x00, 0x4c, 0x00, 0x86, 0x49, 0xa0, 0x03, |
| 0xc0, 0x00, 0xc0, 0x00, 0x00, |
| }; |
| |
| static const TiLp55231Program blink_wipeout1_program = { |
| blink_wipeout1_text, |
| sizeof(blink_wipeout1_text), |
| 0, |
| { 3, 24, 25, } |
| }; |
| |
| static const uint8_t blink_recovery1_text[] = { |
| 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x90, 0x02, |
| 0x94, 0x02, 0x9f, 0x80, 0x98, 255, 0x84, 0x62, |
| 0x9f, 0x81, 0x98, 100, 0x84, 0x62, 0x9f, 0x82, |
| 0x98, 10, 0x84, 0x62, 0x4c, 0x00, 0x86, 0x2c, |
| 0x40, 0x00, 0x9f, 0x81, 0x40, 0x00, 0x9f, 0x80, |
| 0x40, 0x00, 0x4c, 0x00, 0x86, 0x49, 0xa0, 0x03, |
| 0xc0, 0x00, 0xc0, 0x00, 0x00, |
| }; |
| static const TiLp55231Program blink_recovery1_program = { |
| blink_recovery1_text, |
| sizeof(blink_recovery1_text), |
| 0, |
| { 3, 24, 25, } |
| }; |
| |
| /* |
| * fade_in1.src |
| * |
| row_red: dw 0000000000000001b |
| row_green: dw 0000000000000010b |
| row_blue: dw 0000000000000100b |
| |
| .segment program1 |
| mux_map_addr row_red |
| set_pwm 1h |
| trigger s{2|3} |
| end |
| .segment program2 |
| mux_map_addr row_green |
| set_pwm 0h |
| trigger w{1} |
| ramp 2, 50 |
| end |
| .segment program3 |
| mux_map_addr row_blue |
| set_pwm 0h |
| loop3: trigger w{1} |
| ramp 2, 155 |
| end |
| */ |
| |
| static const uint8_t fade_in1_text[] = { |
| 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x9f, 0x80, |
| 0x40, 0x01, 0xe0, 0x0c, 0xc0, 0x00, 0x9f, 0x81, |
| 0x40, 0x00, 0xe0, 0x80, 0x46, 0x32, 0xc0, 0x00, |
| 0x9f, 0x82, 0x40, 0x00, 0xe0, 0x80, 0x34, 0x9B, |
| 0xc0, 0x00, 0x00, |
| }; |
| static const TiLp55231Program fade_in1_program = { |
| fade_in1_text, |
| sizeof(fade_in1_text), |
| 0, |
| { 3, 7, 12, } |
| }; |
| |
| 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} }, |
| {WWR_RECOVERY_PUSHED, {&solid_808080_program} }, |
| {WWR_WIPEOUT_REQUEST, {&blink_wipeout1_program} }, |
| {WWR_RECOVERY_REQUEST, {&blink_recovery1_program} }, |
| {WWR_NORMAL_BOOT, {&fade_in1_program} }, |
| {}, /* Empty record to mark the end of the table. */ |
| }; |