]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/infinity60/led_controller.c
match hal settings to whitefox, fix make rules
[qmk_firmware.git] / keyboards / infinity60 / led_controller.c
1 /*
2 Copyright 2016 flabbergast <s3+flabbergast@sdfeu.org>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19  * LED controller code
20  * WF uses IS31FL3731C matrix LED driver from ISSI
21  * datasheet: http://www.issi.com/WW/pdf/31FL3731C.pdf
22  */
23
24 #include "ch.h"
25 #include "hal.h"
26 #include "print.h"
27
28 #include "led_controller.h"
29
30 #include "suspend.h"
31
32 #include "usb_main.h"
33
34 /* Infinity60 LED MAP
35     - digits mean "row" and "col", i.e. 45 means C4-5 in the IS31 datasheet, matrix A
36
37   11 12 13 14 15 16 17 18 21 22 23 24 25  26 27*
38    28 31 32 33 34 35 36 37 38 41 42 43 44 45
39    46 47 48 51 52 53 54 55 56 57 58 61    62
40     63 64 65 66 67 68 71 72 73 74 75      76 77*
41   78  81  82       83         84  85  86  87
42
43 *Unused in Alphabet Layout
44 */
45
46 /*
47   each page has 0xB4 bytes
48   0 - 0x11: LED control (on/off):
49     order: CA1, CB1, CA2, CB2, .... (CA - matrix A, CB - matrix B)
50       CAn controls Cn-8 .. Cn-1 (LSbit)
51   0x12 - 0x23: blink control (like "LED control")
52   0x24 - 0xB3: PWM control: byte per LED, 0xFF max on
53     order same as above (CA 1st row (8bytes), CB 1st row (8bytes), ...)
54 */
55
56 /* Which LED should be used for CAPS LOCK indicator
57  * The usual Caps Lock position is C4-6, so the address is
58  * 0x24 + (4-1)*0x10 + (8-1) = 0x59 */
59 #if !defined(CAPS_LOCK_LED_ADDRESS)
60 #define CAPS_LOCK_LED_ADDRESS 0x59
61 #endif
62
63 /* Which LED should breathe during sleep */
64 #if !defined(BREATHE_LED_ADDRESS)
65 #define BREATHE_LED_ADDRESS CAPS_LOCK_LED_ADDRESS
66 #endif
67
68 /* =================
69  * ChibiOS I2C setup
70  * ================= */
71 static const I2CConfig i2ccfg = {
72   400000 // clock speed (Hz); 400kHz max for IS31
73 };
74
75 /* ==============
76  *   variables
77  * ============== */
78 // internal communication buffers
79 uint8_t tx[2] __attribute__((aligned(2)));
80 uint8_t rx[1] __attribute__((aligned(2)));
81
82 // buffer for sending the whole page at once (used also as a temp buffer)
83 uint8_t full_page[0xB4+1] = {0};
84
85 // LED mask (which LEDs are present, selected by bits)
86 // See page comment above, control alternates CA matrix/CB matrix
87 // IC60 pcb uses only CA matrix.
88 // Each byte is a control pin for 8 leds 8-1
89 const uint8_t is31_ic60_leds_mask[0x12] = {
90   0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
91   0x00, 0xFF, 0x00, 0xFF, 0x00, 0x7F, 0x00, 0x00, 0x00
92 };
93
94 /* ============================
95  *   communication functions
96  * ============================ */
97 msg_t is31_select_page(uint8_t page) {
98   tx[0] = IS31_COMMANDREGISTER;
99   tx[1] = page;
100   return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 2, NULL, 0, US2ST(IS31_TIMEOUT));
101 }
102
103 msg_t is31_write_data(uint8_t page, uint8_t *buffer, uint8_t size) {
104   is31_select_page(page);
105   return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, buffer, size, NULL, 0, US2ST(IS31_TIMEOUT));
106 }
107
108 msg_t is31_write_register(uint8_t page, uint8_t reg, uint8_t data) {
109   is31_select_page(page);
110   tx[0] = reg;
111   tx[1] = data;
112   return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 2, NULL, 0, US2ST(IS31_TIMEOUT));
113 }
114
115 msg_t is31_read_register(uint8_t b, uint8_t reg, uint8_t *result) {
116   is31_select_page(b);
117
118   tx[0] = reg;
119   return i2cMasterTransmitTimeout(&I2CD1, IS31_ADDR_DEFAULT, tx, 1, result, 1, US2ST(IS31_TIMEOUT));
120 }
121
122 /* ========================
123  * initialise the IS31 chip
124  * ======================== */
125 void is31_init(void) {
126   // just to be sure that it's all zeroes
127   __builtin_memset(full_page,0,0xB4+1);
128   // zero function page, all registers (assuming full_page is all zeroes)
129   is31_write_data(IS31_FUNCTIONREG, full_page, 0xD + 1);
130   // disable hardware shutdown
131   palSetPadMode(GPIOB, 16, PAL_MODE_OUTPUT_PUSHPULL);
132   palSetPad(GPIOB, 16);
133   chThdSleepMilliseconds(10);
134   // software shutdown
135   is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, 0);
136   chThdSleepMilliseconds(10);
137   // zero function page, all registers
138   is31_write_data(IS31_FUNCTIONREG, full_page, 0xD + 1);
139   chThdSleepMilliseconds(10);
140   // software shutdown disable (i.e. turn stuff on)
141   is31_write_register(IS31_FUNCTIONREG, IS31_REG_SHUTDOWN, IS31_REG_SHUTDOWN_ON);
142   chThdSleepMilliseconds(10);
143   // zero all LED registers on all 8 pages
144   uint8_t i;
145   for(i=0; i<8; i++) {
146     is31_write_data(i, full_page, 0xB4 + 1);
147     chThdSleepMilliseconds(1);
148   }
149 }
150
151 /* ==================
152  * LED control thread
153  * ================== */
154 #define LED_MAILBOX_NUM_MSGS 5
155 static msg_t led_mailbox_queue[LED_MAILBOX_NUM_MSGS];
156 mailbox_t led_mailbox;
157 static THD_WORKING_AREA(waLEDthread, 256);
158 static THD_FUNCTION(LEDthread, arg) {
159   (void)arg;
160   chRegSetThreadName("LEDthread");
161
162   uint8_t temp;
163   uint8_t save_page, save_breath1, save_breath2;
164   msg_t msg, retval;
165
166   while(true) {
167     // wait for a message (asynchronous)
168     // (messages are queued (up to LED_MAILBOX_NUM_MSGS) if they can't
169     //  be processed right away)
170     chMBFetch(&led_mailbox, &msg, TIME_INFINITE);
171
172     // process 'msg' here
173     switch(msg) {
174       case LED_MSG_CAPS_ON:
175         // turn caps on on pages 1 and 2
176         is31_write_register(0, CAPS_LOCK_LED_ADDRESS, 0xFF);
177         is31_write_register(1, CAPS_LOCK_LED_ADDRESS, 0xFF);
178         is31_write_register(2, CAPS_LOCK_LED_ADDRESS, 0xFF);
179         break;
180       case LED_MSG_CAPS_OFF:
181         // turn caps off on pages 1 and 2
182         is31_write_register(0, CAPS_LOCK_LED_ADDRESS, 0);
183         is31_write_register(1, CAPS_LOCK_LED_ADDRESS, 0);
184         is31_write_register(2, CAPS_LOCK_LED_ADDRESS, 0);
185         break;
186       case LED_MSG_SLEEP_LED_ON:
187         // save current settings
188         is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &save_page);
189         is31_read_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, &save_breath1);
190         is31_read_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, &save_breath2);
191         // use pages 7 and 8 for (hardware) breathing (assuming they're empty)
192         is31_write_register(6, BREATHE_LED_ADDRESS, 0xFF);
193         is31_write_register(7, BREATHE_LED_ADDRESS, 0x00);
194         is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, (6<<4)|6);
195         is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, IS31_REG_BREATHCTRL2_ENABLE|3);
196         retval = MSG_TIMEOUT;
197         temp = 6;
198         while(retval == MSG_TIMEOUT) {
199           // switch to the other page
200           is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, temp);
201           temp = (temp == 6 ? 7 : 6);
202           // the times should be sufficiently long for IS31 to finish switching pages
203           retval = chMBFetch(&led_mailbox, &msg, MS2ST(temp == 6 ? 4000 : 6000));
204         }
205         // received a message (should be a wakeup), so restore previous state
206         chThdSleepMilliseconds(3000); // need to wait until the page change finishes
207         // note: any other messages are queued
208         is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, save_breath1);
209         is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, save_breath2);
210         is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, save_page);
211         break;
212       case LED_MSG_SLEEP_LED_OFF:
213         // should not get here; wakeup should be received in the branch above
214         break;
215       case LED_MSG_ALL_TOGGLE:
216         // read current page into 'temp'
217         is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp);
218         chThdSleepMilliseconds(1);
219         // switch to 'the other' page
220         if(temp==2) {
221           is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0);
222         } else {
223           is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 2);
224         }
225         break;
226       case LED_MSG_GAME_TOGGLE:
227         // read current page into 'temp'
228         is31_read_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, &temp);
229         chThdSleepMilliseconds(1);
230         // switch to 'the other' page
231         if(temp==1) {
232           is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 0);
233         } else {
234           is31_write_register(IS31_FUNCTIONREG, IS31_REG_PICTDISP, 1);
235         }
236         break;
237     }
238   }
239 }
240
241 //These relate to the LED map above, row and column
242 //0x24 = first byte (CA1) of PWM page, 0x34 is 17th byte (CA2)
243 /* LED game mode */
244 const uint8_t led_game[72] = {
245   0x24,
246   0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247   0x34,
248   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249   0x44,
250   0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251   0x54,
252   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
253   0x64,
254   0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255   0x74,
256   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
257   0x84,
258   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
259   0x94,
260   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
261 };
262
263 /* ALL LEDs */
264 const uint8_t led_all[72] = {
265   0x24,
266   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
267   0x34,
268   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
269 0x44,
270   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
271   0x54,
272   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
273   0x64,
274   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
275   0x74,
276   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
277   0x84,
278   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
279   0x94,
280   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
281 };
282
283 /* =============
284  * hook into TMK
285  * ============= */
286 void led_controller_init(void) {
287   uint8_t i;
288
289   xprintf("led_controller_init");
290   /* initialise I2C */
291   /* I2C pins */
292   palSetPadMode(GPIOB, 0, PAL_MODE_ALTERNATIVE_2); // PTB0/I2C0/SCL
293   palSetPadMode(GPIOB, 1, PAL_MODE_ALTERNATIVE_2); // PTB1/I2C0/SDA
294   /* start I2C */
295   i2cStart(&I2CD1, &i2ccfg);
296   // try high drive (from kiibohd)
297   I2CD1.i2c->C2 |= I2Cx_C2_HDRS;
298   // try glitch fixing (from kiibohd)
299   I2CD1.i2c->FLT = 4;
300
301   chThdSleepMilliseconds(10);
302
303   /* initialise IS31 chip */
304   is31_init();
305
306   /* enable LEDs on all pages */
307   full_page[0] = 0;
308   __builtin_memcpy(full_page+1, is31_ic60_leds_mask, 0x12);
309   for(i=0; i<8; i++) {
310     is31_write_data(i, full_page, 1+0x12);
311   }
312
313   /* enable breathing when the displayed page changes */
314   // Fade-in Fade-out, time = 26ms * 2^N, N=3
315   is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL1, (3<<4)|3);
316   is31_write_register(IS31_FUNCTIONREG, IS31_REG_BREATHCTRL2, IS31_REG_BREATHCTRL2_ENABLE|3);
317
318   /* Write pages */
319   for(i=0; i<8; i++) {
320     is31_write_data(1,(uint8_t *)(led_game+(9*i)),9);
321     chThdSleepMilliseconds(5);
322     is31_write_data(2,(uint8_t *)(led_all+(9*i)),9);
323     chThdSleepMilliseconds(5);
324   }
325
326   // clean up the capslock LED
327   is31_write_register(1, CAPS_LOCK_LED_ADDRESS, 0);
328   is31_write_register(2, CAPS_LOCK_LED_ADDRESS, 0);
329
330   /* more time consuming LED processing should be offloaded into
331    * a thread, with asynchronous messaging. */
332   chMBObjectInit(&led_mailbox, led_mailbox_queue, LED_MAILBOX_NUM_MSGS);
333   chThdCreateStatic(waLEDthread, sizeof(waLEDthread), LOWPRIO, LEDthread, NULL);
334 }
335
336 //TODO: Don't know equivalent QMK hooks for these
337 //
338 //void hook_usb_suspend_entry(void) {
339 //#ifdef SLEEP_LED_ENABLE
340 //  chSysLockFromISR();
341 //  chMBPostI(&led_mailbox, LED_MSG_SLEEP_LED_ON);
342 //  chSysUnlockFromISR();
343 //#endif /* SLEEP_LED_ENABLE */
344 //}
345 //
346 //void hook_usb_suspend_loop(void) {
347 //  chThdSleepMilliseconds(100);
348 //  /* Remote wakeup */
349 //  if((USB_DRIVER.status & 2) && suspend_wakeup_condition()) {
350 //    send_remote_wakeup(&USB_DRIVER);
351 //  }
352 //}
353 //
354 //void hook_usb_wakeup(void) {
355 //#ifdef SLEEP_LED_ENABLE
356 //  chSysLockFromISR();
357 //  chMBPostI(&led_mailbox, LED_MSG_SLEEP_LED_OFF);
358 //  chSysUnlockFromISR();
359 //#endif /* SLEEP_LED_ENABLE */
360 //}
361 //*/