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