summaryrefslogtreecommitdiff
path: root/drivers/led
diff options
context:
space:
mode:
authorHorrorTroll <sonicvipduc@gmail.com>2024-02-16 21:41:35 +0700
committerGitHub <noreply@github.com>2024-02-17 01:41:35 +1100
commitf6709e65ebfd385a9963348d41c19abe9b91e8ad (patch)
treee61190504d666a8535f5e41bed90d8b5cdb7b9dc /drivers/led
parent9b0b3d7b25a845bd1af2152ab1259a6522d5590f (diff)
Add RGB matrix & LED Matrix support for IS31FL3729 (#21944)
Co-authored-by: Xelus22 <preyas22@gmail.com> Co-authored-by: dexter93 <d3xter93@gmail.com>
Diffstat (limited to 'drivers/led')
-rw-r--r--drivers/led/issi/is31fl3729-mono.c220
-rw-r--r--drivers/led/issi/is31fl3729-mono.h265
-rw-r--r--drivers/led/issi/is31fl3729.c226
-rw-r--r--drivers/led/issi/is31fl3729.h267
4 files changed, 978 insertions, 0 deletions
diff --git a/drivers/led/issi/is31fl3729-mono.c b/drivers/led/issi/is31fl3729-mono.c
new file mode 100644
index 0000000000..1617dd40a7
--- /dev/null
+++ b/drivers/led/issi/is31fl3729-mono.c
@@ -0,0 +1,220 @@
1/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
2 * Copyright 2024 Harrison Chan (Xelus)
3 * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "is31fl3729-mono.h"
20#include "i2c_master.h"
21#include "wait.h"
22
23#define IS31FL3729_PWM_REGISTER_COUNT 143
24#define IS31FL3729_SCALING_REGISTER_COUNT 16
25
26#ifndef IS31FL3729_I2C_TIMEOUT
27# define IS31FL3729_I2C_TIMEOUT 100
28#endif
29
30#ifndef IS31FL3729_I2C_PERSISTENCE
31# define IS31FL3729_I2C_PERSISTENCE 0
32#endif
33
34#ifndef IS31FL3729_CONFIGURATION
35# define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9
36#endif
37
38#ifndef IS31FL3729_GLOBAL_CURRENT
39# define IS31FL3729_GLOBAL_CURRENT 0x40
40#endif
41
42#ifndef IS31FL3729_PULLDOWNUP
43# define IS31FL3729_PULLDOWNUP 0x33
44#endif
45
46#ifndef IS31FL3729_SPREAD_SPECTRUM
47# define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SSP_DISABLE
48#endif
49
50#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE
51# define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_RNG_5_PERCENT
52#endif
53
54#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME
55# define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_CLT_1980_US
56#endif
57
58#ifndef IS31FL3729_PWM_FREQUENCY
59# define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ
60#endif
61
62// These buffers match the PWM & scaling registers.
63// Storing them like this is optimal for I2C transfers to the registers.
64typedef struct is31fl3729_driver_t {
65 uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT];
66 bool pwm_buffer_dirty;
67 uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT];
68 bool scaling_buffer_dirty;
69} PACKED is31fl3729_driver_t;
70
71is31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{
72 .pwm_buffer = {0},
73 .pwm_buffer_dirty = false,
74 .scaling_buffer = {0},
75 .scaling_buffer_dirty = false,
76}};
77
78void is31fl3729_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
79#if IS31FL3729_I2C_PERSISTENCE > 0
80 for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) {
81 if (i2c_write_register(addr << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
82 }
83#else
84 i2c_write_register(addr << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT);
85#endif
86}
87
88void is31fl3729_write_pwm_buffer(uint8_t addr, uint8_t index) {
89 // Transmit PWM registers in 9 transfers of 16 bytes.
90
91 // Iterate over the pwm_buffer contents at 16 byte intervals.
92 for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 16) {
93#if IS31FL3729_I2C_PERSISTENCE > 0
94 for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) {
95 if (i2c_write_register(addr << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
96 }
97#else
98 i2c_write_register(addr << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3729_I2C_TIMEOUT);
99#endif
100 }
101}
102
103void is31fl3729_init_drivers(void) {
104 i2c_init();
105
106 is31fl3729_init(IS31FL3729_I2C_ADDRESS_1);
107#if defined(IS31FL3729_I2C_ADDRESS_2)
108 is31fl3729_init(IS31FL3729_I2C_ADDRESS_2);
109# if defined(IS31FL3729_I2C_ADDRESS_3)
110 is31fl3729_init(IS31FL3729_I2C_ADDRESS_3);
111# if defined(IS31FL3729_I2C_ADDRESS_4)
112 is31fl3729_init(IS31FL3729_I2C_ADDRESS_4);
113# endif
114# endif
115#endif
116
117 for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
118 is31fl3729_set_scaling_register(i, 0xFF);
119 }
120
121 is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_1, 0);
122#if defined(IS31FL3729_I2C_ADDRESS_2)
123 is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_2, 1);
124# if defined(IS31FL3729_I2C_ADDRESS_3)
125 is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_3, 2);
126# if defined(IS31FL3729_I2C_ADDRESS_4)
127 is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_4, 3);
128# endif
129# endif
130#endif
131}
132
133void is31fl3729_init(uint8_t addr) {
134 // In order to avoid the LEDs being driven with garbage data
135 // in the LED driver's PWM registers, shutdown is enabled last.
136 // Set up the mode and other settings, clear the PWM registers,
137 // then disable software shutdown.
138
139 // Set Pull up & Down for SWx CSy
140 is31fl3729_write_register(addr, IS31FL3729_REG_PULLDOWNUP, IS31FL3729_PULLDOWNUP);
141
142 // Set Spread Spectrum Register if applicable
143 is31fl3729_write_register(addr, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11));
144
145 // Set PWM Frequency Register if applicable
146 is31fl3729_write_register(addr, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY);
147
148 // Set Golbal Current Control Register
149 is31fl3729_write_register(addr, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT);
150
151 // Set to Normal operation
152 is31fl3729_write_register(addr, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION);
153
154 // Wait 10ms to ensure the device has woken up.
155 wait_ms(10);
156}
157
158void is31fl3729_set_value(int index, uint8_t value) {
159 is31fl3729_led_t led;
160 if (index >= 0 && index < IS31FL3729_LED_COUNT) {
161 memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
162
163 if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {
164 return;
165 }
166
167 driver_buffers[led.driver].pwm_buffer[led.v] = value;
168 driver_buffers[led.driver].pwm_buffer_dirty = true;
169 }
170}
171
172void is31fl3729_set_value_all(uint8_t value) {
173 for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
174 is31fl3729_set_value(i, value);
175 }
176}
177
178void is31fl3729_set_scaling_register(uint8_t index, uint8_t value) {
179 is31fl3729_led_t led;
180 memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
181
182 // need to do a bit of checking here since 3729 scaling is per CS pin.
183 // not the usual per single LED key as per other ISSI drivers
184 // only enable them, since they should be default disabled
185 int cs_value = (led.v & 0x0F) - 1;
186
187 driver_buffers[led.driver].scaling_buffer[cs_value] = value;
188 driver_buffers[led.driver].scaling_buffer_dirty = true;
189}
190
191void is31fl3729_update_pwm_buffers(uint8_t addr, uint8_t index) {
192 if (driver_buffers[index].pwm_buffer_dirty) {
193 is31fl3729_write_pwm_buffer(addr, index);
194
195 driver_buffers[index].pwm_buffer_dirty = false;
196 }
197}
198
199void is31fl3729_update_scaling_registers(uint8_t addr, uint8_t index) {
200 if (driver_buffers[index].scaling_buffer_dirty) {
201 for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) {
202 is31fl3729_write_register(addr, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]);
203 }
204
205 driver_buffers[index].scaling_buffer_dirty = false;
206 }
207}
208
209void is31fl3729_flush(void) {
210 is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_1, 0);
211#if defined(IS31FL3729_I2C_ADDRESS_2)
212 is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_2, 1);
213# if defined(IS31FL3729_I2C_ADDRESS_3)
214 is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_3, 2);
215# if defined(IS31FL3729_I2C_ADDRESS_4)
216 is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_4, 3);
217# endif
218# endif
219#endif
220}
diff --git a/drivers/led/issi/is31fl3729-mono.h b/drivers/led/issi/is31fl3729-mono.h
new file mode 100644
index 0000000000..815c200fd9
--- /dev/null
+++ b/drivers/led/issi/is31fl3729-mono.h
@@ -0,0 +1,265 @@
1/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
2 * Copyright 2024 Harrison Chan (Xelus)
3 * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <stdint.h>
22#include <stdbool.h>
23#include "progmem.h"
24#include "util.h"
25
26#define IS31FL3729_REG_SCALING 0x90
27#define IS31FL3729_REG_CONFIGURATION 0xA0
28#define IS31FL3729_REG_GLOBAL_CURRENT 0xA1
29#define IS31FL3729_REG_PULLDOWNUP 0xB0
30#define IS31FL3729_REG_SPREAD_SPECTRUM 0xB1
31#define IS31FL3729_REG_PWM_FREQUENCY 0xB2
32#define IS31FL3729_REG_RESET 0xCF
33
34#define IS31FL3729_I2C_ADDRESS_GND 0x34
35#define IS31FL3729_I2C_ADDRESS_SCL 0x35
36#define IS31FL3729_I2C_ADDRESS_SDA 0x36
37#define IS31FL3729_I2C_ADDRESS_VCC 0x37
38
39#if defined(LED_MATRIX_IS31FL3729)
40# define IS31FL3729_LED_COUNT LED_MATRIX_LED_COUNT
41#endif
42
43#if defined(IS31FL3729_I2C_ADDRESS_4)
44# define IS31FL3729_DRIVER_COUNT 4
45#elif defined(IS31FL3729_I2C_ADDRESS_3)
46# define IS31FL3729_DRIVER_COUNT 3
47#elif defined(IS31FL3729_I2C_ADDRESS_2)
48# define IS31FL3729_DRIVER_COUNT 2
49#elif defined(IS31FL3729_I2C_ADDRESS_1)
50# define IS31FL3729_DRIVER_COUNT 1
51#endif
52
53typedef struct is31fl3729_led_t {
54 uint8_t driver : 2;
55 uint8_t v;
56} PACKED is31fl3729_led_t;
57
58extern const is31fl3729_led_t PROGMEM g_is31fl3729_leds[IS31FL3729_LED_COUNT];
59
60void is31fl3729_init_drivers(void);
61void is31fl3729_init(uint8_t addr);
62void is31fl3729_write_register(uint8_t addr, uint8_t reg, uint8_t data);
63
64void is31fl3729_set_value(int index, uint8_t value);
65void is31fl3729_set_value_all(uint8_t value);
66
67void is31fl3729_set_scaling_register(uint8_t index, uint8_t value);
68
69// This should not be called from an interrupt
70// (eg. from a timer interrupt).
71// Call this while idle (in between matrix scans).
72// If the buffer is dirty, it will update the driver with the buffer.
73void is31fl3729_update_pwm_buffers(uint8_t addr, uint8_t index);
74void is31fl3729_update_scaling_registers(uint8_t addr, uint8_t index);
75
76void is31fl3729_flush(void);
77
78// Noise reduction using Spread Spectrum register
79#define IS31FL3729_SSP_DISABLE 0b0
80#define IS31FL3729_SSP_ENABLE 0b1
81
82#define IS31FL3729_RNG_5_PERCENT 0b00
83#define IS31FL3729_RNG_15_PERCENT 0b01
84#define IS31FL3729_RNG_24_PERCENT 0b10
85#define IS31FL3729_RNG_34_PERCENT 0b11
86
87#define IS31FL3729_CLT_1980_US 0b00
88#define IS31FL3729_CLT_1200_US 0b01
89#define IS31FL3729_CLT_820_US 0b10
90#define IS31FL3729_CLT_660_US 0b11
91
92// Noise reduction using PWM Frequency register
93#define IS31FL3729_PWM_FREQUENCY_55K_HZ 0b000
94#define IS31FL3729_PWM_FREQUENCY_32K_HZ 0b001
95#define IS31FL3729_PWM_FREQUENCY_4K_HZ 0b010
96#define IS31FL3729_PWM_FREQUENCY_2K_HZ 0b011
97#define IS31FL3729_PWM_FREQUENCY_1K_HZ 0b100
98#define IS31FL3729_PWM_FREQUENCY_500_HZ 0b101
99#define IS31FL3729_PWM_FREQUENCY_250_HZ 0b110
100#define IS31FL3729_PWM_FREQUENCY_80K_HZ 0b111
101
102// Change SWx Setting using Configuration register
103#define IS31FL3729_CONFIG_SWS_15_9 0x01 // 15 CS x 9 SW matrix
104#define IS31FL3729_CONFIG_SWS_16_8 0x11 // 16 CS x 8 SW matrix
105#define IS31FL3729_CONFIG_SWS_16_7 0x21 // 16 CS x 7 SW matrix
106#define IS31FL3729_CONFIG_SWS_16_6 0x31 // 16 CS x 6 SW matrix
107#define IS31FL3729_CONFIG_SWS_16_5 0x41 // 16 CS x 5 SW matrix
108#define IS31FL3729_CONFIG_SWS_16_4 0x51 // 16 CS x 4 SW matrix
109#define IS31FL3729_CONFIG_SWS_16_3 0x61 // 16 CS x 3 SW matrix
110#define IS31FL3729_CONFIG_SWS_16_2 0x71 // 16 CS x 2 SW matrix
111
112// Map CS SW locations to order in PWM / Scaling buffers
113// This matches the ORDER in the Datasheet Register not the POSITION
114// It will always count from 0x01 to (ISSI_MAX_LEDS - 1)
115#define SW1_CS1 0x01
116#define SW1_CS2 0x02
117#define SW1_CS3 0x03
118#define SW1_CS4 0x04
119#define SW1_CS5 0x05
120#define SW1_CS6 0x06
121#define SW1_CS7 0x07
122#define SW1_CS8 0x08
123#define SW1_CS9 0x09
124#define SW1_CS10 0x0A
125#define SW1_CS11 0x0B
126#define SW1_CS12 0x0C
127#define SW1_CS13 0x0D
128#define SW1_CS14 0x0E
129#define SW1_CS15 0x0F
130#define SW1_CS16 0x10
131
132#define SW2_CS1 0x11
133#define SW2_CS2 0x12
134#define SW2_CS3 0x13
135#define SW2_CS4 0x14
136#define SW2_CS5 0x15
137#define SW2_CS6 0x16
138#define SW2_CS7 0x17
139#define SW2_CS8 0x18
140#define SW2_CS9 0x19
141#define SW2_CS10 0x1A
142#define SW2_CS11 0x1B
143#define SW2_CS12 0x1C
144#define SW2_CS13 0x1D
145#define SW2_CS14 0x1E
146#define SW2_CS15 0x1F
147#define SW2_CS16 0x20
148
149#define SW3_CS1 0x21
150#define SW3_CS2 0x22
151#define SW3_CS3 0x23
152#define SW3_CS4 0x24
153#define SW3_CS5 0x25
154#define SW3_CS6 0x26
155#define SW3_CS7 0x27
156#define SW3_CS8 0x28
157#define SW3_CS9 0x29
158#define SW3_CS10 0x2A
159#define SW3_CS11 0x2B
160#define SW3_CS12 0x2C
161#define SW3_CS13 0x2D
162#define SW3_CS14 0x2E
163#define SW3_CS15 0x2F
164#define SW3_CS16 0x30
165
166#define SW4_CS1 0x31
167#define SW4_CS2 0x32
168#define SW4_CS3 0x33
169#define SW4_CS4 0x34
170#define SW4_CS5 0x35
171#define SW4_CS6 0x36
172#define SW4_CS7 0x37
173#define SW4_CS8 0x38
174#define SW4_CS9 0x39
175#define SW4_CS10 0x3A
176#define SW4_CS11 0x3B
177#define SW4_CS12 0x3C
178#define SW4_CS13 0x3D
179#define SW4_CS14 0x3E
180#define SW4_CS15 0x3F
181#define SW4_CS16 0x40
182
183#define SW5_CS1 0x41
184#define SW5_CS2 0x42
185#define SW5_CS3 0x43
186#define SW5_CS4 0x44
187#define SW5_CS5 0x45
188#define SW5_CS6 0x46
189#define SW5_CS7 0x47
190#define SW5_CS8 0x48
191#define SW5_CS9 0x49
192#define SW5_CS10 0x4A
193#define SW5_CS11 0x4B
194#define SW5_CS12 0x4C
195#define SW5_CS13 0x4D
196#define SW5_CS14 0x4E
197#define SW5_CS15 0x4F
198#define SW5_CS16 0x50
199
200#define SW6_CS1 0x51
201#define SW6_CS2 0x52
202#define SW6_CS3 0x53
203#define SW6_CS4 0x54
204#define SW6_CS5 0x55
205#define SW6_CS6 0x56
206#define SW6_CS7 0x57
207#define SW6_CS8 0x58
208#define SW6_CS9 0x59
209#define SW6_CS10 0x5A
210#define SW6_CS11 0x5B
211#define SW6_CS12 0x5C
212#define SW6_CS13 0x5D
213#define SW6_CS14 0x5E
214#define SW6_CS15 0x5F
215#define SW6_CS16 0x60
216
217#define SW7_CS1 0x61
218#define SW7_CS2 0x62
219#define SW7_CS3 0x63
220#define SW7_CS4 0x64
221#define SW7_CS5 0x65
222#define SW7_CS6 0x66
223#define SW7_CS7 0x67
224#define SW7_CS8 0x68
225#define SW7_CS9 0x69
226#define SW7_CS10 0x6A
227#define SW7_CS11 0x6B
228#define SW7_CS12 0x6C
229#define SW7_CS13 0x6D
230#define SW7_CS14 0x6E
231#define SW7_CS15 0x6F
232#define SW7_CS16 0x70
233
234#define SW8_CS1 0x71
235#define SW8_CS2 0x72
236#define SW8_CS3 0x73
237#define SW8_CS4 0x74
238#define SW8_CS5 0x75
239#define SW8_CS6 0x76
240#define SW8_CS7 0x77
241#define SW8_CS8 0x78
242#define SW8_CS9 0x79
243#define SW8_CS10 0x7A
244#define SW8_CS11 0x7B
245#define SW8_CS12 0x7C
246#define SW8_CS13 0x7D
247#define SW8_CS14 0x7E
248#define SW8_CS15 0x7F
249#define SW8_CS16 0x80
250
251#define SW9_CS1 0x81
252#define SW9_CS2 0x82
253#define SW9_CS3 0x83
254#define SW9_CS4 0x84
255#define SW9_CS5 0x85
256#define SW9_CS6 0x86
257#define SW9_CS7 0x87
258#define SW9_CS8 0x88
259#define SW9_CS9 0x89
260#define SW9_CS10 0x8A
261#define SW9_CS11 0x8B
262#define SW9_CS12 0x8C
263#define SW9_CS13 0x8D
264#define SW9_CS14 0x8E
265#define SW9_CS15 0x8F
diff --git a/drivers/led/issi/is31fl3729.c b/drivers/led/issi/is31fl3729.c
new file mode 100644
index 0000000000..06f2d168d0
--- /dev/null
+++ b/drivers/led/issi/is31fl3729.c
@@ -0,0 +1,226 @@
1/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
2 * Copyright 2024 Harrison Chan (Xelus)
3 * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "is31fl3729.h"
20#include "i2c_master.h"
21#include "wait.h"
22
23#define IS31FL3729_PWM_REGISTER_COUNT 143
24#define IS31FL3729_SCALING_REGISTER_COUNT 16
25
26#ifndef IS31FL3729_I2C_TIMEOUT
27# define IS31FL3729_I2C_TIMEOUT 100
28#endif
29
30#ifndef IS31FL3729_I2C_PERSISTENCE
31# define IS31FL3729_I2C_PERSISTENCE 0
32#endif
33
34#ifndef IS31FL3729_CONFIGURATION
35# define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9
36#endif
37
38#ifndef IS31FL3729_GLOBAL_CURRENT
39# define IS31FL3729_GLOBAL_CURRENT 0x40
40#endif
41
42#ifndef IS31FL3729_PULLDOWNUP
43# define IS31FL3729_PULLDOWNUP 0x33
44#endif
45
46#ifndef IS31FL3729_SPREAD_SPECTRUM
47# define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SSP_DISABLE
48#endif
49
50#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE
51# define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_RNG_5_PERCENT
52#endif
53
54#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME
55# define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_CLT_1980_US
56#endif
57
58#ifndef IS31FL3729_PWM_FREQUENCY
59# define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ
60#endif
61
62// These buffers match the PWM & scaling registers.
63// Storing them like this is optimal for I2C transfers to the registers.
64typedef struct is31fl3729_driver_t {
65 uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT];
66 bool pwm_buffer_dirty;
67 uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT];
68 bool scaling_buffer_dirty;
69} PACKED is31fl3729_driver_t;
70
71is31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{
72 .pwm_buffer = {0},
73 .pwm_buffer_dirty = false,
74 .scaling_buffer = {0},
75 .scaling_buffer_dirty = false,
76}};
77
78void is31fl3729_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
79#if IS31FL3729_I2C_PERSISTENCE > 0
80 for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) {
81 if (i2c_write_register(addr << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
82 }
83#else
84 i2c_write_register(addr << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT);
85#endif
86}
87
88void is31fl3729_write_pwm_buffer(uint8_t addr, uint8_t index) {
89 // Transmit PWM registers in 9 transfers of 16 bytes.
90
91 // Iterate over the pwm_buffer contents at 16 byte intervals.
92 for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 16) {
93#if IS31FL3729_I2C_PERSISTENCE > 0
94 for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) {
95 if (i2c_write_register(addr << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
96 }
97#else
98 i2c_write_register(addr << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3729_I2C_TIMEOUT);
99#endif
100 }
101}
102
103void is31fl3729_init_drivers(void) {
104 i2c_init();
105
106 is31fl3729_init(IS31FL3729_I2C_ADDRESS_1);
107#if defined(IS31FL3729_I2C_ADDRESS_2)
108 is31fl3729_init(IS31FL3729_I2C_ADDRESS_2);
109# if defined(IS31FL3729_I2C_ADDRESS_3)
110 is31fl3729_init(IS31FL3729_I2C_ADDRESS_3);
111# if defined(IS31FL3729_I2C_ADDRESS_4)
112 is31fl3729_init(IS31FL3729_I2C_ADDRESS_4);
113# endif
114# endif
115#endif
116
117 for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
118 is31fl3729_set_scaling_register(i, 0xFF, 0xFF, 0xFF);
119 }
120
121 is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_1, 0);
122#if defined(IS31FL3729_I2C_ADDRESS_2)
123 is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_2, 1);
124# if defined(IS31FL3729_I2C_ADDRESS_3)
125 is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_3, 2);
126# if defined(IS31FL3729_I2C_ADDRESS_4)
127 is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_4, 3);
128# endif
129# endif
130#endif
131}
132
133void is31fl3729_init(uint8_t addr) {
134 // In order to avoid the LEDs being driven with garbage data
135 // in the LED driver's PWM registers, shutdown is enabled last.
136 // Set up the mode and other settings, clear the PWM registers,
137 // then disable software shutdown.
138
139 // Set Pull up & Down for SWx CSy
140 is31fl3729_write_register(addr, IS31FL3729_REG_PULLDOWNUP, IS31FL3729_PULLDOWNUP);
141
142 // Set Spread Spectrum Register if applicable
143 is31fl3729_write_register(addr, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11));
144
145 // Set PWM Frequency Register if applicable
146 is31fl3729_write_register(addr, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY);
147
148 // Set Golbal Current Control Register
149 is31fl3729_write_register(addr, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT);
150
151 // Set to Normal operation
152 is31fl3729_write_register(addr, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION);
153
154 // Wait 10ms to ensure the device has woken up.
155 wait_ms(10);
156}
157
158void is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
159 is31fl3729_led_t led;
160 if (index >= 0 && index < IS31FL3729_LED_COUNT) {
161 memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
162
163 if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {
164 return;
165 }
166
167 driver_buffers[led.driver].pwm_buffer[led.r] = red;
168 driver_buffers[led.driver].pwm_buffer[led.g] = green;
169 driver_buffers[led.driver].pwm_buffer[led.b] = blue;
170 driver_buffers[led.driver].pwm_buffer_dirty = true;
171 }
172}
173
174void is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
175 for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {
176 is31fl3729_set_color(i, red, green, blue);
177 }
178}
179
180void is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {
181 is31fl3729_led_t led;
182 memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));
183
184 // need to do a bit of checking here since 3729 scaling is per CS pin.
185 // not the usual per RGB key as per other ISSI drivers
186 // only enable them, since they should be default disabled
187 int cs_red = (led.r & 0x0F) - 1;
188 int cs_green = (led.g & 0x0F) - 1;
189 int cs_blue = (led.b & 0x0F) - 1;
190
191 driver_buffers[led.driver].scaling_buffer[cs_red] = red;
192 driver_buffers[led.driver].scaling_buffer[cs_green] = green;
193 driver_buffers[led.driver].scaling_buffer[cs_blue] = blue;
194 driver_buffers[led.driver].scaling_buffer_dirty = true;
195}
196
197void is31fl3729_update_pwm_buffers(uint8_t addr, uint8_t index) {
198 if (driver_buffers[index].pwm_buffer_dirty) {
199 is31fl3729_write_pwm_buffer(addr, index);
200
201 driver_buffers[index].pwm_buffer_dirty = false;
202 }
203}
204
205void is31fl3729_update_scaling_registers(uint8_t addr, uint8_t index) {
206 if (driver_buffers[index].scaling_buffer_dirty) {
207 for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) {
208 is31fl3729_write_register(addr, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]);
209 }
210
211 driver_buffers[index].scaling_buffer_dirty = false;
212 }
213}
214
215void is31fl3729_flush(void) {
216 is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_1, 0);
217#if defined(IS31FL3729_I2C_ADDRESS_2)
218 is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_2, 1);
219# if defined(IS31FL3729_I2C_ADDRESS_3)
220 is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_3, 2);
221# if defined(IS31FL3729_I2C_ADDRESS_4)
222 is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_4, 3);
223# endif
224# endif
225#endif
226}
diff --git a/drivers/led/issi/is31fl3729.h b/drivers/led/issi/is31fl3729.h
new file mode 100644
index 0000000000..6f2672b6a3
--- /dev/null
+++ b/drivers/led/issi/is31fl3729.h
@@ -0,0 +1,267 @@
1/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>
2 * Copyright 2024 Harrison Chan (Xelus)
3 * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <stdint.h>
22#include <stdbool.h>
23#include "progmem.h"
24#include "util.h"
25
26#define IS31FL3729_REG_SCALING 0x90
27#define IS31FL3729_REG_CONFIGURATION 0xA0
28#define IS31FL3729_REG_GLOBAL_CURRENT 0xA1
29#define IS31FL3729_REG_PULLDOWNUP 0xB0
30#define IS31FL3729_REG_SPREAD_SPECTRUM 0xB1
31#define IS31FL3729_REG_PWM_FREQUENCY 0xB2
32#define IS31FL3729_REG_RESET 0xCF
33
34#define IS31FL3729_I2C_ADDRESS_GND 0x34
35#define IS31FL3729_I2C_ADDRESS_SCL 0x35
36#define IS31FL3729_I2C_ADDRESS_SDA 0x36
37#define IS31FL3729_I2C_ADDRESS_VCC 0x37
38
39#if defined(RGB_MATRIX_IS31FL3729)
40# define IS31FL3729_LED_COUNT RGB_MATRIX_LED_COUNT
41#endif
42
43#if defined(IS31FL3729_I2C_ADDRESS_4)
44# define IS31FL3729_DRIVER_COUNT 4
45#elif defined(IS31FL3729_I2C_ADDRESS_3)
46# define IS31FL3729_DRIVER_COUNT 3
47#elif defined(IS31FL3729_I2C_ADDRESS_2)
48# define IS31FL3729_DRIVER_COUNT 2
49#elif defined(IS31FL3729_I2C_ADDRESS_1)
50# define IS31FL3729_DRIVER_COUNT 1
51#endif
52
53typedef struct is31fl3729_led_t {
54 uint8_t driver : 2;
55 uint8_t r;
56 uint8_t g;
57 uint8_t b;
58} PACKED is31fl3729_led_t;
59
60extern const is31fl3729_led_t PROGMEM g_is31fl3729_leds[IS31FL3729_LED_COUNT];
61
62void is31fl3729_init_drivers(void);
63void is31fl3729_init(uint8_t addr);
64void is31fl3729_write_register(uint8_t addr, uint8_t reg, uint8_t data);
65
66void is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
67void is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
68
69void is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);
70
71// This should not be called from an interrupt
72// (eg. from a timer interrupt).
73// Call this while idle (in between matrix scans).
74// If the buffer is dirty, it will update the driver with the buffer.
75void is31fl3729_update_pwm_buffers(uint8_t addr, uint8_t index);
76void is31fl3729_update_scaling_registers(uint8_t addr, uint8_t index);
77
78void is31fl3729_flush(void);
79
80// Noise reduction using Spread Spectrum register
81#define IS31FL3729_SSP_DISABLE 0b0
82#define IS31FL3729_SSP_ENABLE 0b1
83
84#define IS31FL3729_RNG_5_PERCENT 0b00
85#define IS31FL3729_RNG_15_PERCENT 0b01
86#define IS31FL3729_RNG_24_PERCENT 0b10
87#define IS31FL3729_RNG_34_PERCENT 0b11
88
89#define IS31FL3729_CLT_1980_US 0b00
90#define IS31FL3729_CLT_1200_US 0b01
91#define IS31FL3729_CLT_820_US 0b10
92#define IS31FL3729_CLT_660_US 0b11
93
94// Noise reduction using PWM Frequency register
95#define IS31FL3729_PWM_FREQUENCY_55K_HZ 0b000
96#define IS31FL3729_PWM_FREQUENCY_32K_HZ 0b001
97#define IS31FL3729_PWM_FREQUENCY_4K_HZ 0b010
98#define IS31FL3729_PWM_FREQUENCY_2K_HZ 0b011
99#define IS31FL3729_PWM_FREQUENCY_1K_HZ 0b100
100#define IS31FL3729_PWM_FREQUENCY_500_HZ 0b101
101#define IS31FL3729_PWM_FREQUENCY_250_HZ 0b110
102#define IS31FL3729_PWM_FREQUENCY_80K_HZ 0b111
103
104// Change SWx Setting using Configuration register
105#define IS31FL3729_CONFIG_SWS_15_9 0x01 // 15 CS x 9 SW matrix
106#define IS31FL3729_CONFIG_SWS_16_8 0x11 // 16 CS x 8 SW matrix
107#define IS31FL3729_CONFIG_SWS_16_7 0x21 // 16 CS x 7 SW matrix
108#define IS31FL3729_CONFIG_SWS_16_6 0x31 // 16 CS x 6 SW matrix
109#define IS31FL3729_CONFIG_SWS_16_5 0x41 // 16 CS x 5 SW matrix
110#define IS31FL3729_CONFIG_SWS_16_4 0x51 // 16 CS x 4 SW matrix
111#define IS31FL3729_CONFIG_SWS_16_3 0x61 // 16 CS x 3 SW matrix
112#define IS31FL3729_CONFIG_SWS_16_2 0x71 // 16 CS x 2 SW matrix
113
114// Map CS SW locations to order in PWM / Scaling buffers
115// This matches the ORDER in the Datasheet Register not the POSITION
116// It will always count from 0x01 to (ISSI_MAX_LEDS - 1)
117#define SW1_CS1 0x01
118#define SW1_CS2 0x02
119#define SW1_CS3 0x03
120#define SW1_CS4 0x04
121#define SW1_CS5 0x05
122#define SW1_CS6 0x06
123#define SW1_CS7 0x07
124#define SW1_CS8 0x08
125#define SW1_CS9 0x09
126#define SW1_CS10 0x0A
127#define SW1_CS11 0x0B
128#define SW1_CS12 0x0C
129#define SW1_CS13 0x0D
130#define SW1_CS14 0x0E
131#define SW1_CS15 0x0F
132#define SW1_CS16 0x10
133
134#define SW2_CS1 0x11
135#define SW2_CS2 0x12
136#define SW2_CS3 0x13
137#define SW2_CS4 0x14
138#define SW2_CS5 0x15
139#define SW2_CS6 0x16
140#define SW2_CS7 0x17
141#define SW2_CS8 0x18
142#define SW2_CS9 0x19
143#define SW2_CS10 0x1A
144#define SW2_CS11 0x1B
145#define SW2_CS12 0x1C
146#define SW2_CS13 0x1D
147#define SW2_CS14 0x1E
148#define SW2_CS15 0x1F
149#define SW2_CS16 0x20
150
151#define SW3_CS1 0x21
152#define SW3_CS2 0x22
153#define SW3_CS3 0x23
154#define SW3_CS4 0x24
155#define SW3_CS5 0x25
156#define SW3_CS6 0x26
157#define SW3_CS7 0x27
158#define SW3_CS8 0x28
159#define SW3_CS9 0x29
160#define SW3_CS10 0x2A
161#define SW3_CS11 0x2B
162#define SW3_CS12 0x2C
163#define SW3_CS13 0x2D
164#define SW3_CS14 0x2E
165#define SW3_CS15 0x2F
166#define SW3_CS16 0x30
167
168#define SW4_CS1 0x31
169#define SW4_CS2 0x32
170#define SW4_CS3 0x33
171#define SW4_CS4 0x34
172#define SW4_CS5 0x35
173#define SW4_CS6 0x36
174#define SW4_CS7 0x37
175#define SW4_CS8 0x38
176#define SW4_CS9 0x39
177#define SW4_CS10 0x3A
178#define SW4_CS11 0x3B
179#define SW4_CS12 0x3C
180#define SW4_CS13 0x3D
181#define SW4_CS14 0x3E
182#define SW4_CS15 0x3F
183#define SW4_CS16 0x40
184
185#define SW5_CS1 0x41
186#define SW5_CS2 0x42
187#define SW5_CS3 0x43
188#define SW5_CS4 0x44
189#define SW5_CS5 0x45
190#define SW5_CS6 0x46
191#define SW5_CS7 0x47
192#define SW5_CS8 0x48
193#define SW5_CS9 0x49
194#define SW5_CS10 0x4A
195#define SW5_CS11 0x4B
196#define SW5_CS12 0x4C
197#define SW5_CS13 0x4D
198#define SW5_CS14 0x4E
199#define SW5_CS15 0x4F
200#define SW5_CS16 0x50
201
202#define SW6_CS1 0x51
203#define SW6_CS2 0x52
204#define SW6_CS3 0x53
205#define SW6_CS4 0x54
206#define SW6_CS5 0x55
207#define SW6_CS6 0x56
208#define SW6_CS7 0x57
209#define SW6_CS8 0x58
210#define SW6_CS9 0x59
211#define SW6_CS10 0x5A
212#define SW6_CS11 0x5B
213#define SW6_CS12 0x5C
214#define SW6_CS13 0x5D
215#define SW6_CS14 0x5E
216#define SW6_CS15 0x5F
217#define SW6_CS16 0x60
218
219#define SW7_CS1 0x61
220#define SW7_CS2 0x62
221#define SW7_CS3 0x63
222#define SW7_CS4 0x64
223#define SW7_CS5 0x65
224#define SW7_CS6 0x66
225#define SW7_CS7 0x67
226#define SW7_CS8 0x68
227#define SW7_CS9 0x69
228#define SW7_CS10 0x6A
229#define SW7_CS11 0x6B
230#define SW7_CS12 0x6C
231#define SW7_CS13 0x6D
232#define SW7_CS14 0x6E
233#define SW7_CS15 0x6F
234#define SW7_CS16 0x70
235
236#define SW8_CS1 0x71
237#define SW8_CS2 0x72
238#define SW8_CS3 0x73
239#define SW8_CS4 0x74
240#define SW8_CS5 0x75
241#define SW8_CS6 0x76
242#define SW8_CS7 0x77
243#define SW8_CS8 0x78
244#define SW8_CS9 0x79
245#define SW8_CS10 0x7A
246#define SW8_CS11 0x7B
247#define SW8_CS12 0x7C
248#define SW8_CS13 0x7D
249#define SW8_CS14 0x7E
250#define SW8_CS15 0x7F
251#define SW8_CS16 0x80
252
253#define SW9_CS1 0x81
254#define SW9_CS2 0x82
255#define SW9_CS3 0x83
256#define SW9_CS4 0x84
257#define SW9_CS5 0x85
258#define SW9_CS6 0x86
259#define SW9_CS7 0x87
260#define SW9_CS8 0x88
261#define SW9_CS9 0x89
262#define SW9_CS10 0x8A
263#define SW9_CS11 0x8B
264#define SW9_CS12 0x8C
265#define SW9_CS13 0x8D
266#define SW9_CS14 0x8E
267#define SW9_CS15 0x8F