1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 3 /* 4 * This is a driver for the Whirlwind LED ring, which is equipped with two LED 5 * microcontrollers TI LP55231 (http://www.ti.com/product/lp55231), each of 6 * them driving three multicolor LEDs. 7 * 8 * The only connection between the ring and the main board is an i2c bus. 9 * 10 * This driver imitates a depthcharge display device. On initialization the 11 * driver sets up the controllers to prepare them to accept programs to run. 12 * 13 * When a certain vboot state needs to be indicated, the program for that 14 * state is loaded into the controllers, resulting in the state appropriate 15 * LED behavior. 16 */ 17 18 #include "drivers/i2c/ww_ring/ww_ring_programs.h" 19 20 /**************************************************************** 21 * LED ring program definitions for different patterns. 22 * 23 * Comments below are real lp55231 source code, they are compiled using 24 * lasm.exe, the TI tool available from their Web site (search for lp55231) 25 * and running only on Windows :P. 26 * 27 * Different hex dumps are results of tweaking the source code parameters to 28 * achieve desirable LED ring behavior. It is possible to use just one code 29 * dump and replace certain values in the body to achieve different behaviour 30 * with the same basic dump, but keeping track of location to tweak with every 31 * code change would be quite tedious. 32 */ 33 34 /* 35 * Solid LED display, the arguments of the set_pwm commands set intensity and 36 * color of the display: 37 38 row_red: dw 0000000000000001b 39 row_green: dw 0000000000000010b 40 row_blue: dw 0000000000000100b 41 42 .segment program1 43 mux_map_addr row_red 44 set_pwm 0 45 end 46 47 .segment program2 48 mux_map_addr row_green 49 set_pwm 0 50 end 51 52 .segment program3 53 mux_map_addr row_blue 54 set_pwm 0 55 end 56 */ 57 58 /* RGB set to 000000, resulting in all LEDs off. */ 59 static const uint8_t solid_000000_text[] = { 60 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x9F, 0x80, 61 0x40, 0, 0xC0, 0x00, 0x9F, 0x81, 0x40, 0, 62 0xC0, 0x00, 0x9F, 0x82, 0x40, 0, 0xC0, 0x00 63 }; 64 65 /* Rgb set to 128, resulting in a brightish white color. */ 66 static const uint8_t solid_808080_text[] = { 67 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x9F, 0x80, 68 0x40, 128, 0xC0, 0x00, 0x9F, 0x81, 0x40, 128, 69 0xC0, 0x00, 0x9F, 0x82, 0x40, 128, 0xC0, 0x00 70 }; 71 72 static const TiLp55231Program solid_808080_program = { 73 solid_808080_text, 74 sizeof(solid_808080_text), 75 0, 76 { 3, 6, 9 } 77 }; 78 79 static const TiLp55231Program solid_000000_program = { 80 solid_000000_text, 81 sizeof(solid_000000_text), 82 0, 83 { 3, 6, 9 } 84 }; 85 86 /* 87 * Blinking patterns are trickier then solid ones. 88 * 89 * The three internal engines seem to be competing for resources and get out 90 * of sync in seconds if left running asynchronously. 91 * 92 * When solid patterns are deployed with instantaneous color intensity 93 * changes, all three LEDs can be controlled by one engine in sequential 94 * accesses. But the controllers still need to be synchronized. 95 * 96 * The maximum timer duration of lp55231 is .48 seconds. To achieve longer 97 * blinking intervals the loops delays are deployed. Only the first controller 98 * intervals need to be changed, as the second one is in lockstep with the 99 * first. 100 * 101 * The time granularity is set at .1 second (see commands 'wait 0.1' in the 102 * code), and then the loop counters can be set up to 63 (registers rb and rc), 103 * which allows to generate intervals up to 6.3 seconds in .1 second 104 * increments. 105 */ 106 /* 107 * blink_solid1.src 108 row_red: dw 0000000000000001b 109 row_green: dw 0000000000000010b 110 row_blue: dw 0000000000000100b 111 112 .segment program1 113 ld ra, 2 # LED on duration 114 ld rb, 2 # LED off duration 115 mux_map_addr row_red 116 loop: 117 ld rc, 1 ; red intensity 118 set_pwm rc 119 mux_map_addr row_green 120 ld rc, 50 ; green intensity 121 set_pwm rc 122 mux_map_addr row_blue 123 ld rc, 155 ; blue intensity 124 set_pwm rc 125 wait1: 126 wait 0.1 127 branch ra, wait1 128 set_pwm 0 129 mux_map_addr row_green 130 set_pwm 0 131 mux_map_addr row_red 132 set_pwm 0 133 wait2: 134 wait 0.1 135 branch rb, wait2 136 branch 0, loop 137 .segment program2 138 end 139 .segment program3 140 end 141 */ 142 static const uint8_t blink_wipeout1_text[] = { 143 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x90, 0x02, 144 0x94, 0x02, 0x9f, 0x80, 0x98, 1, 0x84, 0x62, 145 0x9f, 0x81, 0x98, 50, 0x84, 0x62, 0x9f, 0x82, 146 0x98, 155, 0x84, 0x62, 0x4c, 0x00, 0x86, 0x2c, 147 0x40, 0x00, 0x9f, 0x81, 0x40, 0x00, 0x9f, 0x80, 148 0x40, 0x00, 0x4c, 0x00, 0x86, 0x49, 0xa0, 0x03, 149 0xc0, 0x00, 0xc0, 0x00, 0x00, 150 }; 151 152 static const TiLp55231Program blink_wipeout1_program = { 153 blink_wipeout1_text, 154 sizeof(blink_wipeout1_text), 155 0, 156 { 3, 24, 25, } 157 }; 158 159 static const uint8_t blink_recovery1_text[] = { 160 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x90, 0x02, 161 0x94, 0x02, 0x9f, 0x80, 0x98, 255, 0x84, 0x62, 162 0x9f, 0x81, 0x98, 100, 0x84, 0x62, 0x9f, 0x82, 163 0x98, 10, 0x84, 0x62, 0x4c, 0x00, 0x86, 0x2c, 164 0x40, 0x00, 0x9f, 0x81, 0x40, 0x00, 0x9f, 0x80, 165 0x40, 0x00, 0x4c, 0x00, 0x86, 0x49, 0xa0, 0x03, 166 0xc0, 0x00, 0xc0, 0x00, 0x00, 167 }; 168 static const TiLp55231Program blink_recovery1_program = { 169 blink_recovery1_text, 170 sizeof(blink_recovery1_text), 171 0, 172 { 3, 24, 25, } 173 }; 174 175 /* 176 * fade_in1.src 177 * 178 row_red: dw 0000000000000001b 179 row_green: dw 0000000000000010b 180 row_blue: dw 0000000000000100b 181 182 .segment program1 183 mux_map_addr row_red 184 set_pwm 1h 185 trigger s{2|3} 186 end 187 .segment program2 188 mux_map_addr row_green 189 set_pwm 0h 190 trigger w{1} 191 ramp 2, 50 192 end 193 .segment program3 194 mux_map_addr row_blue 195 set_pwm 0h 196 loop3: trigger w{1} 197 ramp 2, 155 198 end 199 */ 200 201 static const uint8_t fade_in1_text[] = { 202 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x9f, 0x80, 203 0x40, 0x01, 0xe0, 0x0c, 0xc0, 0x00, 0x9f, 0x81, 204 0x40, 0x00, 0xe0, 0x80, 0x46, 0x32, 0xc0, 0x00, 205 0x9f, 0x82, 0x40, 0x00, 0xe0, 0x80, 0x34, 0x9B, 206 0xc0, 0x00, 0x00, 207 }; 208 static const TiLp55231Program fade_in1_program = { 209 fade_in1_text, 210 sizeof(fade_in1_text), 211 0, 212 { 3, 7, 12, } 213 }; 214 215 const WwRingStateProg wwr_state_programs[] = { 216 /* 217 * for test purposes the blank screen program is set to blinking, will 218 * be changed soon. 219 */ 220 {WWR_ALL_OFF, {&solid_000000_program} }, 221 {WWR_RECOVERY_PUSHED, {&solid_808080_program} }, 222 {WWR_WIPEOUT_REQUEST, {&blink_wipeout1_program} }, 223 {WWR_RECOVERY_REQUEST, {&blink_recovery1_program} }, 224 {WWR_NORMAL_BOOT, {&fade_in1_program} }, 225 {}, /* Empty record to mark the end of the table. */ 226 }; 227