Updated rgb_led struct field modifier to flags (#5619)
[jackhill/qmk/firmware.git] / tmk_core / protocol / arm_atsam / led_matrix.c
CommitLineData
30680c6e 1/*
2Copyright 2018 Massdrop Inc.
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along 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"
763b26cd 20#include "rgb_matrix.h"
30680c6e 21#include <string.h>
d55dc9b8 22#include <math.h>
30680c6e 23
763b26cd
DP
24#ifdef USE_MASSDROP_CONFIGURATOR
25__attribute__((weak))
26led_instruction_t led_instructions[] = { { .end = 1 } };
27static void led_matrix_massdrop_config_override(int i);
28#endif // USE_MASSDROP_CONFIGURATOR
29
30extern rgb_config_t rgb_matrix_config;
31extern rgb_counters_t g_rgb_counters;
32
30680c6e 33void 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
41void 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
62issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
63
763b26cd
DP
64issi3733_led_t led_map[ISSI3733_LED_COUNT] = ISSI3733_LED_MAP;
65RGB led_buffer[ISSI3733_LED_COUNT];
30680c6e 66
67uint8_t gcr_desired;
30680c6e 68uint8_t gcr_actual;
69uint8_t gcr_actual_last;
763b26cd
DP
70#ifdef USE_MASSDROP_CONFIGURATOR
71uint8_t gcr_breathe;
72float breathe_mult;
73float pomod;
74#endif
30680c6e 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
82static uint8_t gcr_min_counter;
83static uint8_t v_5v_cat_hit;
84
85//WARNING: Automatic GCR is in place to prevent USB shutdown and LED driver overloading
86void gcr_compute(void)
87{
88 uint8_t action = ACT_GCR_NONE;
763b26cd 89 uint8_t gcr_use = gcr_desired;
30680c6e 90
763b26cd 91#ifdef USE_MASSDROP_CONFIGURATOR
30680c6e 92 if (led_animation_breathing)
763b26cd 93 {
30680c6e 94 gcr_use = gcr_breathe;
763b26cd
DP
95 }
96#endif
30680c6e 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
763b26cd 170#ifdef USE_MASSDROP_CONFIGURATOR
30680c6e 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 }
763b26cd 180#endif
30680c6e 181 }
182 }
183}
184
30680c6e 185void 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
763b26cd 197 for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
30680c6e 198 {
199 //BYTE: 1 + (SW-1)*16 + (CS-1)
763b26cd
DP
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));
30680c6e 203
204 //BYTE: 1 + (SW-1)*2 + (CS-1)/8
205 //BIT: (CS-1)%8
763b26cd
DP
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));
30680c6e 209 }
210}
211
763b26cd 212void led_matrix_prepare(void)
30680c6e 213{
763b26cd 214 for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
30680c6e 215 {
763b26cd
DP
216 *led_map[i].rgb.r = 0;
217 *led_map[i].rgb.g = 0;
218 *led_map[i].rgb.b = 0;
30680c6e 219 }
30680c6e 220}
221
763b26cd 222void led_set_one(int i, uint8_t r, uint8_t g, uint8_t b)
30680c6e 223{
763b26cd 224 if (i < ISSI3733_LED_COUNT)
30680c6e 225 {
763b26cd
DP
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
30680c6e 233 }
234}
235
763b26cd 236void led_set_all(uint8_t r, uint8_t g, uint8_t b)
30680c6e 237{
763b26cd
DP
238 for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
239 {
240 led_set_one(i, r, g, b);
241 }
30680c6e 242}
243
763b26cd 244void init(void)
30680c6e 245{
763b26cd 246 DBGC(DC_LED_MATRIX_INIT_BEGIN);
30680c6e 247
763b26cd 248 issi3733_prepare_arrays();
30680c6e 249
763b26cd 250 led_matrix_prepare();
30680c6e 251
763b26cd
DP
252 gcr_min_counter = 0;
253 v_5v_cat_hit = 0;
30680c6e 254
763b26cd
DP
255 DBGC(DC_LED_MATRIX_INIT_COMPLETE);
256}
30680c6e 257
763b26cd
DP
258void 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
30680c6e 265
763b26cd
DP
266 // Wait for previous transfer to complete
267 while (i2c_led_q_running) {}
30680c6e 268
763b26cd
DP
269 // Copy buffer to live DMA region
270 for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
30680c6e 271 {
763b26cd
DP
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;
30680c6e 275 }
276
763b26cd
DP
277#ifdef USE_MASSDROP_CONFIGURATOR
278 breathe_mult = 1;
30680c6e 279
763b26cd 280 if (led_animation_breathing)
30680c6e 281 {
763b26cd
DP
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 }
30680c6e 295
763b26cd
DP
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;
30680c6e 301
763b26cd 302#endif // USE_MASSDROP_CONFIGURATOR
30680c6e 303
763b26cd 304 uint8_t drvid;
30680c6e 305
763b26cd
DP
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 }
30680c6e 313
763b26cd
DP
314 for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
315 I2C_LED_Q_PWM(drvid); //Queue data
30680c6e 316
763b26cd
DP
317 i2c_led_q_run();
318}
30680c6e 319
763b26cd
DP
320void 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++)
30680c6e 326 {
30680c6e 327 if (
763b26cd
DP
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))
30680c6e 344 {
763b26cd
DP
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;
30680c6e 348 }
349 }
30680c6e 350 }
30680c6e 351
763b26cd 352}
30680c6e 353
763b26cd
DP
354const 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
369uint8_t led_animation_orientation = 0;
370uint8_t led_animation_direction = 0;
371uint8_t led_animation_breathing = 0;
372uint8_t led_animation_id = 0;
373float led_animation_speed = 4.0f;
374uint8_t led_lighting_mode = LED_MODE_NORMAL;
375uint8_t led_enabled = 1;
376uint8_t led_animation_breathe_cur = BREATHE_MIN_STEP;
377uint8_t breathe_dir = 1;
378
379static void led_run_pattern(led_setup_t *f, float* ro, float* go, float* bo, float pos) {
380 float po;
30680c6e 381
763b26cd
DP
382 while (f->end != 1)
383 {
384 po = pos; //Reset po for new frame
30680c6e 385
763b26cd
DP
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;
30680c6e 390
763b26cd
DP
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;
30680c6e 397
763b26cd
DP
398 if (po > 100) po -= 100;
399 else if (po < 0) po += 100;
400 }
30680c6e 401
763b26cd
DP
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
30680c6e 406
763b26cd
DP
407 //Calculate the po within the start-stop percentage for color blending
408 po = (po - f->hs) / (f->he - f->hs);
30680c6e 409
763b26cd
DP
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 }
63e212c0 429
763b26cd
DP
430 f++;
431 }
63e212c0
SD
432}
433
763b26cd 434static void led_matrix_massdrop_config_override(int i)
30680c6e 435{
763b26cd
DP
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
a7113c8e 446 if (led_lighting_mode == LED_MODE_KEYS_ONLY && HAS_FLAGS(g_rgb_leds[i].flags, LED_FLAG_UNDERGLOW)) {
763b26cd 447 //Do not act on this LED
a7113c8e 448 } else if (led_lighting_mode == LED_MODE_NON_KEYS_ONLY && !HAS_FLAGS(g_rgb_leds[i].flags, LED_FLAG_UNDERGLOW)) {
763b26cd
DP
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 }
30680c6e 460
763b26cd
DP
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 }
30680c6e 470
763b26cd
DP
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);
30680c6e 479 }
480
763b26cd
DP
481 next_iter:
482 led_cur_instruction++;
483 }
30680c6e 484
763b26cd
DP
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;
30680c6e 488
763b26cd
DP
489 if (led_animation_breathing)
490 {
491 ro *= breathe_mult;
492 go *= breathe_mult;
493 bo *= breathe_mult;
30680c6e 494 }
495 }
496
763b26cd
DP
497 led_buffer[i].r = (uint8_t)ro;
498 led_buffer[i].g = (uint8_t)go;
499 led_buffer[i].b = (uint8_t)bo;
30680c6e 500}
501
763b26cd 502#endif // USE_MASSDROP_CONFIGURATOR