1 /*
2 * Copyright (c) 2023 Endor AG
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT issi_is31fl3216a
8
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/drivers/led.h>
11 #include <zephyr/logging/log.h>
12
13 #define IS31FL3216A_REG_CONFIG 0x00
14 #define IS31FL3216A_REG_CTL_1 0x01
15 #define IS31FL3216A_REG_CTL_2 0x02
16 #define IS31FL3216A_REG_LIGHT_EFFECT 0x03
17 #define IS31FL3216A_REG_CHANNEL_CONFIG 0x04
18 #define IS31FL3216A_REG_GPIO_CONFIG 0x05
19 #define IS31FL3216A_REG_OUTPUT_PORT 0x06
20 #define IS31FL3216A_REG_INT_CONTROL 0x07
21 #define IS31FL3216A_REG_ADC_SAMPLE_RATE 0x09
22 #define IS31FL3216A_REG_PWM_FIRST 0x10
23 #define IS31FL3216A_REG_PWM_LAST 0x1F
24 #define IS31FL3216A_REG_UPDATE 0xB0
25 #define IS31FL3216A_REG_FRAME_DELAY 0xB6
26 #define IS31FL3216A_REG_FRAME_START 0xB7
27
28 #define IS31FL3216A_MAX_LEDS 16
29
30 LOG_MODULE_REGISTER(is31fl3216a, CONFIG_LED_LOG_LEVEL);
31
32 struct is31fl3216a_cfg {
33 struct i2c_dt_spec i2c;
34 };
35
is31fl3216a_write_buffer(const struct i2c_dt_spec * i2c,const uint8_t * buffer,uint32_t num_bytes)36 static int is31fl3216a_write_buffer(const struct i2c_dt_spec *i2c,
37 const uint8_t *buffer, uint32_t num_bytes)
38 {
39 int status;
40
41 status = i2c_write_dt(i2c, buffer, num_bytes);
42 if (status < 0) {
43 LOG_ERR("Could not write buffer: %i", status);
44 return status;
45 }
46
47 return 0;
48 }
49
is31fl3216a_write_reg(const struct i2c_dt_spec * i2c,uint8_t reg,uint8_t val)50 static int is31fl3216a_write_reg(const struct i2c_dt_spec *i2c, uint8_t reg,
51 uint8_t val)
52 {
53 uint8_t buffer[2] = {reg, val};
54
55 return is31fl3216a_write_buffer(i2c, buffer, sizeof(buffer));
56 }
57
is31fl3216a_update_pwm(const struct i2c_dt_spec * i2c)58 static int is31fl3216a_update_pwm(const struct i2c_dt_spec *i2c)
59 {
60 return is31fl3216a_write_reg(i2c, IS31FL3216A_REG_UPDATE, 0);
61 }
62
is31fl3216a_brightness_to_pwm(uint8_t brightness)63 static uint8_t is31fl3216a_brightness_to_pwm(uint8_t brightness)
64 {
65 return (0xFFU * brightness) / 100;
66 }
67
is31fl3216a_led_write_channels(const struct device * dev,uint32_t start_channel,uint32_t num_channels,const uint8_t * buf)68 static int is31fl3216a_led_write_channels(const struct device *dev,
69 uint32_t start_channel,
70 uint32_t num_channels,
71 const uint8_t *buf)
72 {
73 const struct is31fl3216a_cfg *config = dev->config;
74 uint8_t i2c_buffer[IS31FL3216A_MAX_LEDS + 1];
75 int status;
76 int i;
77
78 if (num_channels == 0) {
79 return 0;
80 }
81
82 if (start_channel + num_channels > IS31FL3216A_MAX_LEDS) {
83 return -EINVAL;
84 }
85
86 /* Invert channels, last register is channel 0 */
87 i2c_buffer[0] = IS31FL3216A_REG_PWM_LAST - start_channel -
88 (num_channels - 1);
89 for (i = 0; i < num_channels; i++) {
90 if (buf[num_channels - i - 1] > 100) {
91 return -EINVAL;
92 }
93 i2c_buffer[i + 1] = is31fl3216a_brightness_to_pwm(
94 buf[num_channels - i - 1]);
95 }
96
97 status = is31fl3216a_write_buffer(&config->i2c, i2c_buffer,
98 num_channels + 1);
99 if (status < 0) {
100 return status;
101 }
102
103 return is31fl3216a_update_pwm(&config->i2c);
104 }
105
is31fl3216a_led_set_brightness(const struct device * dev,uint32_t led,uint8_t value)106 static int is31fl3216a_led_set_brightness(const struct device *dev,
107 uint32_t led, uint8_t value)
108 {
109 const struct is31fl3216a_cfg *config = dev->config;
110 uint8_t pwm_reg = IS31FL3216A_REG_PWM_LAST - led;
111 int status;
112 uint8_t pwm_value;
113
114 if (led > IS31FL3216A_MAX_LEDS - 1 || value > 100) {
115 return -EINVAL;
116 }
117
118 pwm_value = is31fl3216a_brightness_to_pwm(value);
119 status = is31fl3216a_write_reg(&config->i2c, pwm_reg, pwm_value);
120 if (status < 0) {
121 return status;
122 }
123
124 return is31fl3216a_update_pwm(&config->i2c);
125 }
126
is31fl3216a_led_on(const struct device * dev,uint32_t led)127 static int is31fl3216a_led_on(const struct device *dev, uint32_t led)
128 {
129 return is31fl3216a_led_set_brightness(dev, led, 100);
130 }
131
is31fl3216a_led_off(const struct device * dev,uint32_t led)132 static int is31fl3216a_led_off(const struct device *dev, uint32_t led)
133 {
134 return is31fl3216a_led_set_brightness(dev, led, 0);
135 }
136
is31fl3216a_init_registers(const struct i2c_dt_spec * i2c)137 static int is31fl3216a_init_registers(const struct i2c_dt_spec *i2c)
138 {
139 int i;
140 int status;
141
142 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_CTL_1, 0xFF);
143 if (status < 0) {
144 return status;
145 }
146
147 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_CTL_2, 0xFF);
148 if (status < 0) {
149 return status;
150 }
151
152 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_LIGHT_EFFECT, 0x00);
153 if (status < 0) {
154 return status;
155 }
156
157 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_CHANNEL_CONFIG,
158 0x00);
159 if (status < 0) {
160 return status;
161 }
162
163 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_GPIO_CONFIG, 0x00);
164 if (status < 0) {
165 return status;
166 }
167
168 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_OUTPUT_PORT, 0x00);
169 if (status < 0) {
170 return status;
171 }
172
173 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_INT_CONTROL, 0x00);
174 if (status < 0) {
175 return status;
176 }
177
178 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_ADC_SAMPLE_RATE,
179 0x00);
180 if (status < 0) {
181 return status;
182 }
183
184 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_FRAME_DELAY, 0x00);
185 if (status < 0) {
186 return status;
187 }
188
189 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_FRAME_START, 0x00);
190 if (status < 0) {
191 return status;
192 }
193
194 for (i = IS31FL3216A_REG_PWM_FIRST;
195 i <= IS31FL3216A_REG_PWM_LAST;
196 i++) {
197 status = is31fl3216a_write_reg(i2c, i, 0);
198 if (status < 0) {
199 return status;
200 }
201 }
202
203 status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_UPDATE, 0);
204 if (status < 0) {
205 return status;
206 }
207
208 return is31fl3216a_write_reg(i2c, IS31FL3216A_REG_CONFIG, 0x00);
209 }
210
is31fl3216a_init(const struct device * dev)211 static int is31fl3216a_init(const struct device *dev)
212 {
213 const struct is31fl3216a_cfg *config = dev->config;
214
215 LOG_DBG("Initializing @0x%x...", config->i2c.addr);
216
217 if (!i2c_is_ready_dt(&config->i2c)) {
218 LOG_ERR("I2C device not ready");
219 return -ENODEV;
220 }
221
222 return is31fl3216a_init_registers(&config->i2c);
223 }
224
225 static DEVICE_API(led, is31fl3216a_led_api) = {
226 .set_brightness = is31fl3216a_led_set_brightness,
227 .on = is31fl3216a_led_on,
228 .off = is31fl3216a_led_off,
229 .write_channels = is31fl3216a_led_write_channels
230 };
231
232 #define IS31FL3216A_INIT(id) \
233 static const struct is31fl3216a_cfg is31fl3216a_##id##_cfg = { \
234 .i2c = I2C_DT_SPEC_INST_GET(id), \
235 }; \
236 DEVICE_DT_INST_DEFINE(id, &is31fl3216a_init, NULL, NULL, \
237 &is31fl3216a_##id##_cfg, POST_KERNEL, \
238 CONFIG_LED_INIT_PRIORITY, &is31fl3216a_led_api);
239
240 DT_INST_FOREACH_STATUS_OKAY(IS31FL3216A_INIT)
241