summaryrefslogtreecommitdiff
path: root/drivers/led/issi/is31flcommon.c
blob: 048db286b6233c0b3f9bb4d7153c5a517e7bbfe7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/* Copyright 2017 Jason Williams
 * Copyright 2018 Jack Humbert
 * Copyright 2018 Yiancar
 * Copyright 2020 MelGeek
 * Copyright 2021 MasterSpoon
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "is31flcommon.h"
#include "i2c_master.h"
#include "wait.h"
#include <string.h>

// Set defaults for Timeout and Persistence
#ifndef ISSI_TIMEOUT
#    define ISSI_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
#    define ISSI_PERSISTENCE 0
#endif

uint8_t i2c_transfer_buffer[20];

// These buffers match the PWM & scaling registers.
// Storing them like this is optimal for I2C transfers to the registers.
uint8_t g_pwm_buffer[DRIVER_COUNT][ISSI_MAX_LEDS];
bool    g_pwm_buffer_update_required[DRIVER_COUNT] = {false};

uint8_t g_scaling_buffer[DRIVER_COUNT][ISSI_SCALING_SIZE];
bool    g_scaling_buffer_update_required[DRIVER_COUNT] = {false};

// For writing of single register entry
void IS31FL_write_single_register(uint8_t addr, uint8_t reg, uint8_t data) {
    // Set register address and register data ready to write
    i2c_transfer_buffer[0] = reg;
    i2c_transfer_buffer[1] = data;

#if ISSI_PERSISTENCE > 0
    for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
        if (i2c_transmit(addr << 1, i2c_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
    }
#else
    i2c_transmit(addr << 1, i2c_transfer_buffer, 2, ISSI_TIMEOUT);
#endif
}

// For writing of mulitple register entries to make use of address auto increment
// Once the controller has been called and we have written the first bit of data
// the controller will move to the next register meaning we can write sequential blocks.
bool IS31FL_write_multi_registers(uint8_t addr, uint8_t *source_buffer, uint8_t buffer_size, uint8_t transfer_size, uint8_t start_reg_addr) {
    // Split the buffer into chunks to transfer
    for (int i = 0; i < buffer_size; i += transfer_size) {
        // Set the first entry of transfer buffer to the first register we want to write
        i2c_transfer_buffer[0] = i + start_reg_addr;
        // Copy the section of our source buffer into the transfer buffer after first register address
        memcpy(i2c_transfer_buffer + 1, source_buffer + i, transfer_size);

#if ISSI_PERSISTENCE > 0
        for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
            if (i2c_transmit(addr << 1, i2c_transfer_buffer, transfer_size + 1, ISSI_TIMEOUT) != 0) {
                return false;
            }
        }
#else
        if (i2c_transmit(addr << 1, i2c_transfer_buffer, transfer_size + 1, ISSI_TIMEOUT) != 0) {
            return false;
        }
#endif
    }
    return true;
}

void IS31FL_unlock_register(uint8_t addr, uint8_t page) {
    // unlock the command register and select Page to write
    IS31FL_write_single_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, ISSI_REGISTER_UNLOCK);
    IS31FL_write_single_register(addr, ISSI_COMMANDREGISTER, page);
}

void IS31FL_common_init(uint8_t addr, uint8_t ssr) {
    // Setup phase, need to take out of software shutdown and configure
    // ISSI_SSR_x is passed to allow Master / Slave setting where applicable

    // Unlock the command register & select Function Register
    IS31FL_unlock_register(addr, ISSI_PAGE_FUNCTION);
    // Set Configuration Register to remove Software shutdown
    IS31FL_write_single_register(addr, ISSI_REG_CONFIGURATION, ISSI_CONFIGURATION);
    // Set Golbal Current Control Register
    IS31FL_write_single_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
    // Set Pull up & Down for SWx CSy
    IS31FL_write_single_register(addr, ISSI_REG_PULLDOWNUP, ISSI_PULLDOWNUP);
// Set Tempature Status
#ifdef ISSI_REG_TEMP
    IS31FL_write_single_register(addr, ISSI_REG_TEMP, ISSI_TEMP);
#endif
    // Set Spread Spectrum Register, passed through as sets SYNC function
    IS31FL_write_single_register(addr, ISSI_REG_SSR, ssr);
// Set PWM Frequency Enable Register if applicable
#ifdef ISSI_REG_PWM_ENABLE
    IS31FL_write_single_register(addr, ISSI_REG_PWM_ENABLE, ISSI_PWM_ENABLE);
#endif
// Set PWM Frequency Register if applicable
#ifdef ISSI_REG_PWM_SET
    IS31FL_write_single_register(addr, ISSI_REG_PWM_SET, ISSI_PWM_SET);
#endif

    // Wait 10ms to ensure the device has woken up.
    wait_ms(10);
}

void IS31FL_common_update_pwm_register(uint8_t addr, uint8_t index) {
    if (g_pwm_buffer_update_required[index]) {
        // Queue up the correct page
        IS31FL_unlock_register(addr, ISSI_PAGE_PWM);
        // Hand off the update to IS31FL_write_multi_registers
        IS31FL_write_multi_registers(addr, g_pwm_buffer[index], ISSI_MAX_LEDS, ISSI_PWM_TRF_SIZE, ISSI_PWM_REG_1ST);
        // Update flags that pwm_buffer has been updated
        g_pwm_buffer_update_required[index] = false;
    }
}

#ifdef ISSI_MANUAL_SCALING
void IS31FL_set_manual_scaling_buffer(void) {
    is31_led led;
    is31_led scale;
    for (int i = 0; i < ISSI_MANUAL_SCALING; i++) {
        memcpy_P(&scale, (&g_is31_scaling[i]), sizeof(scale));

#    ifdef RGB_MATRIX_ENABLE
        if (scale.driver >= 0 && scale.driver < RGB_MATRIX_LED_COUNT) {
            memcpy_P(&led, (&g_is31_leds[scale.driver]), sizeof(led));

            if (g_scaling_buffer[led.driver][led.r] = scale.r && g_scaling_buffer[led.driver][led.g] = scale.g && g_scaling_buffer[led.driver][led.b] = scale.b) {
                return;
            }
            g_scaling_buffer[led.driver][led.r] = scale.r;
            g_scaling_buffer[led.driver][led.g] = scale.g;
            g_scaling_buffer[led.driver][led.b] = scale.b;
#    elif defined(LED_MATRIX_ENABLE)
        if (scale.driver >= 0 && scale.driver < LED_MATRIX_LED_COUNT) {
            memcpy_P(&led, (&g_is31_leds[scale.driver]), sizeof(led));

            if (g_scaling_buffer[led.driver][led.v] == scale.v) {
                return;
            }
            g_scaling_buffer[led.driver][led.v] = scale.v;
#    endif
            g_scaling_buffer_update_required[led.driver] = true;
        }
    }
}
#endif

void IS31FL_common_update_scaling_register(uint8_t addr, uint8_t index) {
    if (g_scaling_buffer_update_required[index]) {
        // Queue up the correct page
        IS31FL_unlock_register(addr, ISSI_PAGE_SCALING);
        // Hand off the update to IS31FL_write_multi_registers
        IS31FL_write_multi_registers(addr, g_scaling_buffer[index], ISSI_SCALING_SIZE, ISSI_SCALING_TRF_SIZE, ISSI_SCL_REG_1ST);
        // Update flags that scaling_buffer has been updated
        g_scaling_buffer_update_required[index] = false;
    }
}

void IS31FL_common_flush(void) {
    IS31FL_common_update_pwm_register(DRIVER_ADDR_1, 0);
#if defined(DRIVER_ADDR_2)
    IS31FL_common_update_pwm_register(DRIVER_ADDR_2, 1);
#    if defined(DRIVER_ADDR_3)
    IS31FL_common_update_pwm_register(DRIVER_ADDR_3, 2);
#        if defined(DRIVER_ADDR_4)
    IS31FL_common_update_pwm_register(DRIVER_ADDR_4, 3);
#        endif
#    endif
#endif
}

#ifdef RGB_MATRIX_ENABLE
void IS31FL_RGB_init_drivers(void) {
    i2c_init();

    IS31FL_common_init(DRIVER_ADDR_1, ISSI_SSR_1);
#    if defined(DRIVER_ADDR_2)
    IS31FL_common_init(DRIVER_ADDR_2, ISSI_SSR_2);
#        if defined(DRIVER_ADDR_3)
    IS31FL_common_init(DRIVER_ADDR_3, ISSI_SSR_3);
#            if defined(DRIVER_ADDR_4)
    IS31FL_common_init(DRIVER_ADDR_4, ISSI_SSR_4);
#            endif
#        endif
#    endif

    for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
        IS31FL_RGB_set_scaling_buffer(i, true, true, true);
    }

    // This actually updates the LED drivers
#    ifdef ISSI_MANUAL_SCALING
    IS31FL_set_manual_scaling_buffer();
#    endif

    IS31FL_common_update_scaling_register(DRIVER_ADDR_1, 0);
#    if defined(DRIVER_ADDR_2)
    IS31FL_common_update_scaling_register(DRIVER_ADDR_2, 1);
#        if defined(DRIVER_ADDR_3)
    IS31FL_common_update_scaling_register(DRIVER_ADDR_3, 2);
#            if defined(DRIVER_ADDR_4)
    IS31FL_common_update_scaling_register(DRIVER_ADDR_4, 3);
#            endif
#        endif
#    endif
}

// Colour is set by adjusting PWM register
void IS31FL_RGB_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
    if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
        is31_led led;
        memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));

        g_pwm_buffer[led.driver][led.r]          = red;
        g_pwm_buffer[led.driver][led.g]          = green;
        g_pwm_buffer[led.driver][led.b]          = blue;
        g_pwm_buffer_update_required[led.driver] = true;
    }
}

void IS31FL_RGB_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
    for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
        IS31FL_RGB_set_color(i, red, green, blue);
    }
}

// Setup Scaling register that decides the peak current of each LED
void IS31FL_RGB_set_scaling_buffer(uint8_t index, bool red, bool green, bool blue) {
    is31_led led;
    memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
    if (red) {
        g_scaling_buffer[led.driver][led.r] = ISSI_SCAL_RED;
    } else {
        g_scaling_buffer[led.driver][led.r] = ISSI_SCAL_RED_OFF;
    }
    if (green) {
        g_scaling_buffer[led.driver][led.g] = ISSI_SCAL_GREEN;
    } else {
        g_scaling_buffer[led.driver][led.g] = ISSI_SCAL_GREEN_OFF;
    }
    if (blue) {
        g_scaling_buffer[led.driver][led.b] = ISSI_SCAL_BLUE;
    } else {
        g_scaling_buffer[led.driver][led.b] = ISSI_SCAL_BLUE_OFF;
    }
    g_scaling_buffer_update_required[led.driver] = true;
}

#elif defined(LED_MATRIX_ENABLE)
// LED Matrix Specific scripts
void IS31FL_simple_init_drivers(void) {
    i2c_init();

    IS31FL_common_init(DRIVER_ADDR_1, ISSI_SSR_1);
#    if defined(DRIVER_ADDR_2)
    IS31FL_common_init(DRIVER_ADDR_2, ISSI_SSR_2);
#        if defined(DRIVER_ADDR_3)
    IS31FL_common_init(DRIVER_ADDR_3, ISSI_SSR_3);
#            if defined(DRIVER_ADDR_4)
    IS31FL_common_init(DRIVER_ADDR_4, ISSI_SSR_4);
#            endif
#        endif
#    endif

    for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
        IS31FL_simple_set_scaling_buffer(i, true);
    }

// This actually updates the LED drivers
#    ifdef ISSI_MANUAL_SCALING
    IS31FL_set_manual_scaling_buffer();
#    endif

    IS31FL_common_update_scaling_register(DRIVER_ADDR_1, 0);
#    if defined(DRIVER_ADDR_2)
    IS31FL_common_update_scaling_register(DRIVER_ADDR_2, 1);
#        if defined(DRIVER_ADDR_3)
    IS31FL_common_update_scaling_register(DRIVER_ADDR_3, 2);
#            if defined(DRIVER_ADDR_4)
    IS31FL_common_update_scaling_register(DRIVER_ADDR_4, 3);
#            endif
#        endif
#    endif
}

void IS31FL_simple_set_scaling_buffer(uint8_t index, bool value) {
    is31_led led;
    memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
    if (value) {
        g_scaling_buffer[led.driver][led.v] = ISSI_SCAL_LED;
    } else {
        g_scaling_buffer[led.driver][led.v] = ISSI_SCAL_LED_OFF;
    }
    g_scaling_buffer_update_required[led.driver] = true;
}

void IS31FL_simple_set_brightness(int index, uint8_t value) {
    if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
        is31_led led;
        memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));

        g_pwm_buffer[led.driver][led.v] = value;
        g_pwm_buffer_update_required[led.driver] = true;
    }
}

void IS31FL_simple_set_brigntness_all(uint8_t value) {
    for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
        IS31FL_simple_set_brightness(i, value);
    }
}
#endif