/* 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 instanteneous color intensity * changes, all three LEDs can be controlled by one engine in sequential * accesses. But the controllers still neeed 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. */ };