]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/protocol/arm_atsam/led_matrix.c
Add layout description to keymap.c (#5810)
[qmk_firmware.git] / tmk_core / protocol / arm_atsam / led_matrix.c
1 /*
2 Copyright 2018 Massdrop Inc.
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 #include "arm_atsam_protocol.h"
19 #include "tmk_core/common/led.h"
20 #include "rgb_matrix.h"
21 #include <string.h>
22 #include <math.h>
23
24 #ifdef USE_MASSDROP_CONFIGURATOR
25 __attribute__((weak))
26 led_instruction_t led_instructions[] = { { .end = 1 } };
27 static void led_matrix_massdrop_config_override(int i);
28 #endif // USE_MASSDROP_CONFIGURATOR
29
30 extern rgb_config_t rgb_matrix_config;
31 extern rgb_counters_t g_rgb_counters;
32
33 void SERCOM1_0_Handler( void )
34 {
35     if (SERCOM1->I2CM.INTFLAG.bit.ERROR)
36     {
37         SERCOM1->I2CM.INTFLAG.reg = SERCOM_I2CM_INTENCLR_ERROR;
38     }
39 }
40
41 void DMAC_0_Handler( void )
42 {
43     if (DMAC->Channel[0].CHINTFLAG.bit.TCMPL)
44     {
45         DMAC->Channel[0].CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL;
46
47         i2c1_stop();
48
49         i2c_led_q_running = 0;
50
51         i2c_led_q_run();
52
53         return;
54     }
55
56     if (DMAC->Channel[0].CHINTFLAG.bit.TERR)
57     {
58         DMAC->Channel[0].CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
59     }
60 }
61
62 issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
63
64 issi3733_led_t led_map[ISSI3733_LED_COUNT] = ISSI3733_LED_MAP;
65 RGB led_buffer[ISSI3733_LED_COUNT];
66
67 uint8_t gcr_desired;
68 uint8_t gcr_actual;
69 uint8_t gcr_actual_last;
70 #ifdef USE_MASSDROP_CONFIGURATOR
71 uint8_t gcr_breathe;
72 float breathe_mult;
73 float pomod;
74 #endif
75
76 #define ACT_GCR_NONE    0
77 #define ACT_GCR_INC     1
78 #define ACT_GCR_DEC     2
79
80 #define LED_GCR_STEP_AUTO 2
81
82 static uint8_t gcr_min_counter;
83 static uint8_t v_5v_cat_hit;
84
85 //WARNING: Automatic GCR is in place to prevent USB shutdown and LED driver overloading
86 void gcr_compute(void)
87 {
88     uint8_t action = ACT_GCR_NONE;
89     uint8_t gcr_use = gcr_desired;
90
91 #ifdef USE_MASSDROP_CONFIGURATOR
92     if (led_animation_breathing)
93     {
94         gcr_use = gcr_breathe;
95     }
96 #endif
97
98     //If the 5v takes a catastrophic hit, disable the LED drivers briefly, assert auto gcr mode, min gcr and let the auto take over
99     if (v_5v < V5_CAT)
100     {
101         I2C3733_Control_Set(0);
102         //CDC_print("USB: WARNING: 5V catastrophic level reached! Disabling LED drivers!\r\n"); //Blocking print is bad here!
103         v_5v_cat_hit = 20; //~100ms recover
104         gcr_actual = 0; //Minimize GCR
105         usb_gcr_auto = 1; //Force auto mode enabled
106         return;
107     }
108     else if (v_5v_cat_hit > 1)
109     {
110         v_5v_cat_hit--;
111         return;
112     }
113     else if (v_5v_cat_hit == 1)
114     {
115         I2C3733_Control_Set(1);
116         CDC_print("USB: WARNING: Re-enabling LED drivers\r\n");
117         v_5v_cat_hit = 0;
118         return;
119     }
120
121     if (usb_gcr_auto)
122     {
123         if (v_5v_avg < V5_LOW) action = ACT_GCR_DEC;
124         else if (v_5v_avg > V5_HIGH && gcr_actual < gcr_use) action = ACT_GCR_INC;
125         else if (gcr_actual > gcr_use) action = ACT_GCR_DEC;
126     }
127     else
128     {
129         if (gcr_actual < gcr_use) action = ACT_GCR_INC;
130         else if (gcr_actual > gcr_use) action = ACT_GCR_DEC;
131     }
132
133     if (action == ACT_GCR_NONE)
134     {
135         gcr_min_counter = 0;
136     }
137     else if (action == ACT_GCR_INC)
138     {
139         if (LED_GCR_STEP_AUTO > LED_GCR_MAX - gcr_actual) gcr_actual = LED_GCR_MAX; //Obey max and prevent wrapping
140         else gcr_actual += LED_GCR_STEP_AUTO;
141         gcr_min_counter = 0;
142     }
143     else if (action == ACT_GCR_DEC)
144     {
145         if (LED_GCR_STEP_AUTO > gcr_actual) //Prevent wrapping
146         {
147             gcr_actual = 0;
148             //At this point, power can no longer be cut from the LED drivers, so focus on cutting out extra port if active
149             if (usb_extra_state != USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) //If not in a wait for replug state
150             {
151                 if (usb_extra_state == USB_EXTRA_STATE_ENABLED) //If extra usb is enabled
152                 {
153                     gcr_min_counter++;
154                     if (gcr_min_counter > 200) //5ms per check = 1s delay
155                     {
156                         USB_ExtraSetState(USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG);
157                         usb_extra_manual = 0; //Force disable manual mode of extra port
158                         if (usb_extra_manual) CDC_print("USB: Disabling extra port until replug and manual mode toggle!\r\n");
159                         else CDC_print("USB: Disabling extra port until replug!\r\n");
160                     }
161                 }
162             }
163         }
164         else
165         {
166             //Power successfully cut back from LED drivers
167             gcr_actual -= LED_GCR_STEP_AUTO;
168             gcr_min_counter = 0;
169
170 #ifdef USE_MASSDROP_CONFIGURATOR
171             //If breathe mode is active, the top end can fluctuate if the host can not supply enough current
172             //So set the breathe GCR to where it becomes stable
173             if (led_animation_breathing == 1)
174             {
175                 gcr_breathe = gcr_actual;
176                 //PS: At this point, setting breathing to exhale makes a noticebly shorter cycle
177                 //    and the same would happen maybe one or two more times. Therefore I'm favoring
178                 //    powering through one full breathe and letting gcr settle completely
179             }
180 #endif
181         }
182     }
183 }
184
185 void issi3733_prepare_arrays(void)
186 {
187     memset(issidrv,0,sizeof(issi3733_driver_t) * ISSI3733_DRIVER_COUNT);
188
189     int i;
190     uint8_t addrs[ISSI3733_DRIVER_COUNT] = ISSI3773_DRIVER_ADDRESSES;
191
192     for (i=0;i<ISSI3733_DRIVER_COUNT;i++)
193     {
194         issidrv[i].addr = addrs[i];
195     }
196
197     for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
198     {
199         //BYTE: 1 + (SW-1)*16 + (CS-1)
200         led_map[i].rgb.g = issidrv[led_map[i].adr.drv-1].pwm + 1 + ((led_map[i].adr.swg-1)*16 + (led_map[i].adr.cs-1));
201         led_map[i].rgb.r = issidrv[led_map[i].adr.drv-1].pwm + 1 + ((led_map[i].adr.swr-1)*16 + (led_map[i].adr.cs-1));
202         led_map[i].rgb.b = issidrv[led_map[i].adr.drv-1].pwm + 1 + ((led_map[i].adr.swb-1)*16 + (led_map[i].adr.cs-1));
203
204         //BYTE: 1 + (SW-1)*2 + (CS-1)/8
205         //BIT: (CS-1)%8
206         *(issidrv[led_map[i].adr.drv-1].onoff + 1 + (led_map[i].adr.swg-1)*2+(led_map[i].adr.cs-1)/8) |= (1<<((led_map[i].adr.cs-1)%8));
207         *(issidrv[led_map[i].adr.drv-1].onoff + 1 + (led_map[i].adr.swr-1)*2+(led_map[i].adr.cs-1)/8) |= (1<<((led_map[i].adr.cs-1)%8));
208         *(issidrv[led_map[i].adr.drv-1].onoff + 1 + (led_map[i].adr.swb-1)*2+(led_map[i].adr.cs-1)/8) |= (1<<((led_map[i].adr.cs-1)%8));
209     }
210 }
211
212 void led_matrix_prepare(void)
213 {
214     for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
215     {
216         *led_map[i].rgb.r = 0;
217         *led_map[i].rgb.g = 0;
218         *led_map[i].rgb.b = 0;
219     }
220 }
221
222 void led_set_one(int i, uint8_t r, uint8_t g, uint8_t b)
223 {
224     if (i < ISSI3733_LED_COUNT)
225     {
226 #ifdef USE_MASSDROP_CONFIGURATOR
227         led_matrix_massdrop_config_override(i);
228 #else
229         led_buffer[i].r = r;
230         led_buffer[i].g = g;
231         led_buffer[i].b = b;
232 #endif
233     }
234 }
235
236 void led_set_all(uint8_t r, uint8_t g, uint8_t b)
237 {
238   for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
239   {
240     led_set_one(i, r, g, b);
241   }
242 }
243
244 void init(void)
245 {
246     DBGC(DC_LED_MATRIX_INIT_BEGIN);
247
248     issi3733_prepare_arrays();
249
250     led_matrix_prepare();
251
252     gcr_min_counter = 0;
253     v_5v_cat_hit = 0;
254
255     DBGC(DC_LED_MATRIX_INIT_COMPLETE);
256 }
257
258 void flush(void)
259 {
260 #ifdef USE_MASSDROP_CONFIGURATOR
261     if (!led_enabled) { return; } //Prevent calculations and I2C traffic if LED drivers are not enabled
262 #else
263     if (!sr_exp_data.bit.SDB_N) { return; } //Prevent calculations and I2C traffic if LED drivers are not enabled
264 #endif
265
266     // Wait for previous transfer to complete
267     while (i2c_led_q_running) {}
268
269     // Copy buffer to live DMA region
270     for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
271     {
272         *led_map[i].rgb.r = led_buffer[i].r;
273         *led_map[i].rgb.g = led_buffer[i].g;
274         *led_map[i].rgb.b = led_buffer[i].b;
275     }
276
277 #ifdef USE_MASSDROP_CONFIGURATOR
278     breathe_mult = 1;
279
280     if (led_animation_breathing)
281     {
282         //+60us 119 LED
283         led_animation_breathe_cur += BREATHE_STEP * breathe_dir;
284
285         if (led_animation_breathe_cur >= BREATHE_MAX_STEP)
286             breathe_dir = -1;
287         else if (led_animation_breathe_cur <= BREATHE_MIN_STEP)
288             breathe_dir = 1;
289
290         //Brightness curve created for 256 steps, 0 - ~98%
291         breathe_mult = 0.000015 * led_animation_breathe_cur * led_animation_breathe_cur;
292         if (breathe_mult > 1) breathe_mult = 1;
293         else if (breathe_mult < 0) breathe_mult = 0;
294     }
295
296     //This should only be performed once per frame
297     pomod = (float)((g_rgb_counters.tick / 10) % (uint32_t)(1000.0f / led_animation_speed)) / 10.0f * led_animation_speed;
298     pomod *= 100.0f;
299     pomod = (uint32_t)pomod % 10000;
300     pomod /= 100.0f;
301
302 #endif // USE_MASSDROP_CONFIGURATOR
303
304     uint8_t drvid;
305
306     //NOTE: GCR does not need to be timed with LED processing, but there is really no harm
307     if (gcr_actual != gcr_actual_last)
308     {
309         for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
310             I2C_LED_Q_GCR(drvid); //Queue data
311         gcr_actual_last = gcr_actual;
312     }
313
314     for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
315         I2C_LED_Q_PWM(drvid); //Queue data
316
317     i2c_led_q_run();
318 }
319
320 void led_matrix_indicators(void)
321 {
322     uint8_t kbled = keyboard_leds();
323     if (kbled && rgb_matrix_config.enable)
324     {
325         for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
326         {
327             if (
328             #if USB_LED_NUM_LOCK_SCANCODE != 255
329                 (led_map[i].scan == USB_LED_NUM_LOCK_SCANCODE && (kbled & (1<<USB_LED_NUM_LOCK))) ||
330             #endif //NUM LOCK
331             #if USB_LED_CAPS_LOCK_SCANCODE != 255
332                 (led_map[i].scan == USB_LED_CAPS_LOCK_SCANCODE && (kbled & (1<<USB_LED_CAPS_LOCK))) ||
333             #endif //CAPS LOCK
334             #if USB_LED_SCROLL_LOCK_SCANCODE != 255
335                 (led_map[i].scan == USB_LED_SCROLL_LOCK_SCANCODE && (kbled & (1<<USB_LED_SCROLL_LOCK))) ||
336             #endif //SCROLL LOCK
337             #if USB_LED_COMPOSE_SCANCODE != 255
338                 (led_map[i].scan == USB_LED_COMPOSE_SCANCODE && (kbled & (1<<USB_LED_COMPOSE))) ||
339             #endif //COMPOSE
340             #if USB_LED_KANA_SCANCODE != 255
341                 (led_map[i].scan == USB_LED_KANA_SCANCODE && (kbled & (1<<USB_LED_KANA))) ||
342             #endif //KANA
343             (0))
344             {
345                 led_buffer[i].r = 255 - led_buffer[i].r;
346                 led_buffer[i].g = 255 - led_buffer[i].g;
347                 led_buffer[i].b = 255 - led_buffer[i].b;
348             }
349         }
350     }
351
352 }
353
354 const rgb_matrix_driver_t rgb_matrix_driver = {
355   .init = init,
356   .flush = flush,
357   .set_color = led_set_one,
358   .set_color_all = led_set_all
359 };
360
361 /*==============================================================================
362 =                           Legacy Lighting Support                            =
363 ==============================================================================*/
364
365 #ifdef USE_MASSDROP_CONFIGURATOR
366 // Ported from Massdrop QMK Github Repo
367
368 // TODO?: wire these up to keymap.c
369 uint8_t led_animation_orientation = 0;
370 uint8_t led_animation_direction = 0;
371 uint8_t led_animation_breathing = 0;
372 uint8_t led_animation_id = 0;
373 float led_animation_speed = 4.0f;
374 uint8_t led_lighting_mode = LED_MODE_NORMAL;
375 uint8_t led_enabled = 1;
376 uint8_t led_animation_breathe_cur = BREATHE_MIN_STEP;
377 uint8_t breathe_dir = 1;
378
379 static void led_run_pattern(led_setup_t *f, float* ro, float* go, float* bo, float pos) {
380     float po;
381
382     while (f->end != 1)
383     {
384         po = pos; //Reset po for new frame
385
386         //Add in any moving effects
387         if ((!led_animation_direction && f->ef & EF_SCR_R) || (led_animation_direction && (f->ef & EF_SCR_L)))
388         {
389             po -= pomod;
390
391             if (po > 100) po -= 100;
392             else if (po < 0) po += 100;
393         }
394         else if ((!led_animation_direction && f->ef & EF_SCR_L) || (led_animation_direction && (f->ef & EF_SCR_R)))
395         {
396             po += pomod;
397
398             if (po > 100) po -= 100;
399             else if (po < 0) po += 100;
400         }
401
402         //Check if LED's po is in current frame
403         if (po < f->hs) { f++; continue; }
404         if (po > f->he) { f++; continue; }
405         //note: < 0 or > 100 continue
406
407         //Calculate the po within the start-stop percentage for color blending
408         po = (po - f->hs) / (f->he - f->hs);
409
410         //Add in any color effects
411         if (f->ef & EF_OVER)
412         {
413             *ro = (po * (f->re - f->rs)) + f->rs;// + 0.5;
414             *go = (po * (f->ge - f->gs)) + f->gs;// + 0.5;
415             *bo = (po * (f->be - f->bs)) + f->bs;// + 0.5;
416         }
417         else if (f->ef & EF_SUBTRACT)
418         {
419             *ro -= (po * (f->re - f->rs)) + f->rs;// + 0.5;
420             *go -= (po * (f->ge - f->gs)) + f->gs;// + 0.5;
421             *bo -= (po * (f->be - f->bs)) + f->bs;// + 0.5;
422         }
423         else
424         {
425             *ro += (po * (f->re - f->rs)) + f->rs;// + 0.5;
426             *go += (po * (f->ge - f->gs)) + f->gs;// + 0.5;
427             *bo += (po * (f->be - f->bs)) + f->bs;// + 0.5;
428         }
429
430         f++;
431     }
432 }
433
434 static void led_matrix_massdrop_config_override(int i)
435 {
436     float ro = 0;
437     float go = 0;
438     float bo = 0;
439
440     float po = (led_animation_orientation)
441         ? (float)g_rgb_leds[i].point.y / 64.f * 100
442         : (float)g_rgb_leds[i].point.x / 224.f * 100;
443
444     uint8_t highest_active_layer = biton32(layer_state);
445
446     if (led_lighting_mode == LED_MODE_KEYS_ONLY && HAS_FLAGS(g_rgb_leds[i].flags, LED_FLAG_UNDERGLOW)) {
447         //Do not act on this LED
448     } else if (led_lighting_mode == LED_MODE_NON_KEYS_ONLY && !HAS_FLAGS(g_rgb_leds[i].flags, LED_FLAG_UNDERGLOW)) {
449         //Do not act on this LED
450     } else if (led_lighting_mode == LED_MODE_INDICATORS_ONLY) {
451         //Do not act on this LED (Only show indicators)
452     } else {
453         led_instruction_t* led_cur_instruction = led_instructions;
454         while (!led_cur_instruction->end) {
455             // Check if this applies to current layer
456             if ((led_cur_instruction->flags & LED_FLAG_MATCH_LAYER) &&
457                 (led_cur_instruction->layer != highest_active_layer)) {
458                 goto next_iter;
459             }
460
461             // Check if this applies to current index
462             if (led_cur_instruction->flags & LED_FLAG_MATCH_ID) {
463                 uint8_t modid = i / 32;                                     //Calculate which id# contains the led bit
464                 uint32_t modidbit = 1 << (i % 32);                          //Calculate the bit within the id#
465                 uint32_t *bitfield = &led_cur_instruction->id0 + modid;     //Add modid as offset to id0 address. *bitfield is now idX of the led id
466                 if (~(*bitfield) & modidbit) {                              //Check if led bit is not set in idX
467                     goto next_iter;
468                 }
469             }
470
471             if (led_cur_instruction->flags & LED_FLAG_USE_RGB) {
472                 ro = led_cur_instruction->r;
473                 go = led_cur_instruction->g;
474                 bo = led_cur_instruction->b;
475             } else if (led_cur_instruction->flags & LED_FLAG_USE_PATTERN) {
476                 led_run_pattern(led_setups[led_cur_instruction->pattern_id], &ro, &go, &bo, po);
477             } else if (led_cur_instruction->flags & LED_FLAG_USE_ROTATE_PATTERN) {
478                 led_run_pattern(led_setups[led_animation_id], &ro, &go, &bo, po);
479             }
480
481             next_iter:
482                 led_cur_instruction++;
483         }
484
485         if (ro > 255) ro = 255; else if (ro < 0) ro = 0;
486         if (go > 255) go = 255; else if (go < 0) go = 0;
487         if (bo > 255) bo = 255; else if (bo < 0) bo = 0;
488
489         if (led_animation_breathing)
490         {
491             ro *= breathe_mult;
492             go *= breathe_mult;
493             bo *= breathe_mult;
494         }
495     }
496
497     led_buffer[i].r = (uint8_t)ro;
498     led_buffer[i].g = (uint8_t)go;
499     led_buffer[i].b = (uint8_t)bo;
500 }
501
502 #endif // USE_MASSDROP_CONFIGURATOR