1 /*
2 * Copyright 2022-2023 Daniel DeGrasse <daniel@degrasse.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #define DT_DRV_COMPAT issi_is31fl3733
7
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/drivers/led.h>
11 #include <zephyr/kernel.h>
12
13 #include <zephyr/drivers/led/is31fl3733.h>
14
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(is31fl3733, CONFIG_LED_LOG_LEVEL);
17
18 /* IS31FL3733 register definitions */
19 #define CMD_SEL_REG 0xFD /* Command/page selection reg */
20 #define CMD_SEL_LED 0x0 /* LED configuration page */
21 #define CMD_SEL_PWM 0x1 /* PWM configuration page */
22 #define CMD_SEL_FUNC 0x3 /* Function configuration page */
23
24 #define CMD_LOCK_REG 0xFE /* Command selection lock reg */
25 #define CMD_LOCK_UNLOCK 0xC5 /* Command sel unlock value */
26
27 /* IS31FL3733 page specific register definitions */
28
29 /* Function configuration page */
30 #define CONF_REG 0x0 /* configuration register */
31 #define CONF_REG_SSD_MASK 0x1 /* Software shutdown mask */
32 #define CONF_REG_SSD_SHIFT 0x0 /* Software shutdown shift */
33 #define CONF_REG_SYNC_SHIFT 0x6 /* Sync mode shift */
34 #define CONF_REG_SYNC_MASK 0xC /* Sync mode mask */
35
36 #define GLOBAL_CURRENT_CTRL_REG 0x1 /* global current control register */
37
38 #define RESET_REG 0x11 /* Reset all registers to POR state */
39
40 /* Matrix Layout definitions */
41 #define IS31FL3733_ROW_COUNT 12
42 #define IS31FL3733_COL_COUNT 16
43 #define IS31FL3733_MAX_LED (IS31FL3733_ROW_COUNT * IS31FL3733_COL_COUNT)
44
45 /* Max brightness */
46 #define IS31FL3733_MAX_BRIGHTNESS 100
47
48 struct is31fl3733_config {
49 struct i2c_dt_spec bus;
50 struct gpio_dt_spec sdb;
51 uint8_t current_limit;
52 uint8_t sync;
53 };
54
55 struct is31fl3733_data {
56 /* Active configuration page */
57 uint32_t selected_page;
58 /* Scratch buffer, used for bulk controller writes */
59 uint8_t scratch_buf[IS31FL3733_MAX_LED + 1];
60 /* LED config reg state, IS31FL3733 conf reg is write only */
61 uint8_t conf_reg;
62 };
63
64 /* Selects target register page for IS31FL3733. After setting the
65 * target page, all I2C writes will use the selected page until the selected
66 * page is changed.
67 */
is31fl3733_select_page(const struct device * dev,uint8_t page)68 static int is31fl3733_select_page(const struct device *dev, uint8_t page)
69 {
70 const struct is31fl3733_config *config = dev->config;
71 struct is31fl3733_data *data = dev->data;
72 int ret = 0U;
73
74 if (data->selected_page == page) {
75 /* No change necessary */
76 return 0;
77 }
78
79 /* Unlock page selection register */
80 ret = i2c_reg_write_byte_dt(&config->bus, CMD_LOCK_REG, CMD_LOCK_UNLOCK);
81 if (ret < 0) {
82 LOG_ERR("Could not unlock page selection register");
83 return ret;
84 }
85
86 /* Write to function select to select active page */
87 ret = i2c_reg_write_byte_dt(&config->bus, CMD_SEL_REG, page);
88 if (ret < 0) {
89 LOG_ERR("Could not select active page");
90 return ret;
91 }
92 data->selected_page = page;
93
94 return ret;
95 }
96
is31fl3733_led_set_brightness(const struct device * dev,uint32_t led,uint8_t value)97 static int is31fl3733_led_set_brightness(const struct device *dev, uint32_t led, uint8_t value)
98 {
99 const struct is31fl3733_config *config = dev->config;
100 int ret;
101 uint8_t led_brightness = (uint8_t)(((uint32_t)value * 255) / 100);
102
103 if (led >= IS31FL3733_MAX_LED) {
104 return -EINVAL;
105 }
106
107 /* Configure PWM mode */
108 ret = is31fl3733_select_page(dev, CMD_SEL_PWM);
109 if (ret < 0) {
110 return ret;
111 }
112
113 return i2c_reg_write_byte_dt(&config->bus, led, led_brightness);
114 }
115
is31fl3733_led_on(const struct device * dev,uint32_t led)116 static int is31fl3733_led_on(const struct device *dev, uint32_t led)
117 {
118 return is31fl3733_led_set_brightness(dev, led, IS31FL3733_MAX_BRIGHTNESS);
119 }
120
is31fl3733_led_off(const struct device * dev,uint32_t led)121 static int is31fl3733_led_off(const struct device *dev, uint32_t led)
122 {
123 return is31fl3733_led_set_brightness(dev, led, 0);
124 }
125
is31fl3733_led_write_channels(const struct device * dev,uint32_t start_channel,uint32_t num_channels,const uint8_t * buf)126 static int is31fl3733_led_write_channels(const struct device *dev, uint32_t start_channel,
127 uint32_t num_channels, const uint8_t *buf)
128 {
129 const struct is31fl3733_config *config = dev->config;
130 struct is31fl3733_data *data = dev->data;
131 int ret = 0U;
132 uint8_t *pwm_start;
133
134 if ((start_channel + num_channels) > IS31FL3733_MAX_LED) {
135 return -EINVAL;
136 }
137 pwm_start = data->scratch_buf + start_channel;
138 /* Set PWM and LED target registers as first byte of each transfer */
139 *pwm_start = start_channel;
140 memcpy((pwm_start + 1), buf, num_channels);
141
142 /* Write LED PWM states */
143 ret = is31fl3733_select_page(dev, CMD_SEL_PWM);
144 if (ret < 0) {
145 return ret;
146 }
147 LOG_HEXDUMP_DBG(pwm_start, (num_channels + 1), "PWM states");
148
149 return i2c_write_dt(&config->bus, pwm_start, num_channels + 1);
150 }
151
is31fl3733_init(const struct device * dev)152 static int is31fl3733_init(const struct device *dev)
153 {
154 const struct is31fl3733_config *config = dev->config;
155 struct is31fl3733_data *data = dev->data;
156 int ret = 0U;
157 uint8_t dummy;
158
159 if (!i2c_is_ready_dt(&config->bus)) {
160 LOG_ERR("I2C device not ready");
161 return -ENODEV;
162 }
163 if (config->sdb.port != NULL) {
164 if (!gpio_is_ready_dt(&config->sdb)) {
165 LOG_ERR("GPIO SDB pin not ready");
166 return -ENODEV;
167 }
168 /* Set SDB pin high to exit hardware shutdown */
169 ret = gpio_pin_configure_dt(&config->sdb, GPIO_OUTPUT_ACTIVE);
170 if (ret < 0) {
171 return ret;
172 }
173 }
174
175 ret = is31fl3733_select_page(dev, CMD_SEL_FUNC);
176 if (ret < 0) {
177 return ret;
178 }
179 /*
180 * read reset reg to reset all registers to POR state,
181 * in case we are booting from a warm reset.
182 */
183 ret = i2c_reg_read_byte_dt(&config->bus, RESET_REG, &dummy);
184 if (ret < 0) {
185 return ret;
186 }
187
188 /* Select function page after LED controller reset */
189 ret = is31fl3733_select_page(dev, CMD_SEL_FUNC);
190 if (ret < 0) {
191 return ret;
192 }
193 /* Set global current control register based off devicetree value */
194 ret = i2c_reg_write_byte_dt(&config->bus, GLOBAL_CURRENT_CTRL_REG,
195 config->current_limit);
196 if (ret < 0) {
197 return ret;
198 }
199 /* As a final step, we exit software shutdown, disabling display
200 * blanking. We also set the LED controller sync mode here.
201 */
202 data->conf_reg = (config->sync << CONF_REG_SYNC_SHIFT) | CONF_REG_SSD_MASK;
203 ret = i2c_reg_write_byte_dt(&config->bus, CONF_REG, data->conf_reg);
204 if (ret < 0) {
205 return ret;
206 }
207
208 /* Enable all LEDs. We only control LED brightness in this driver. */
209 data->scratch_buf[0] = 0x0;
210 memset(data->scratch_buf + 1, 0xFF, (IS31FL3733_MAX_LED / 8));
211 ret = is31fl3733_select_page(dev, CMD_SEL_LED);
212 if (ret < 0) {
213 return ret;
214 }
215
216 return i2c_write_dt(&config->bus, data->scratch_buf,
217 (IS31FL3733_MAX_LED / 8) + 1);
218 }
219
220 /* Custom IS31FL3733 specific APIs */
221
222 /**
223 * @brief Blanks IS31FL3733 LED display.
224 *
225 * When blank_en is set, the LED display will be disabled. This can be used for
226 * flicker-free display updates, or power saving.
227 *
228 * @param dev: LED device structure
229 * @param blank_en: should blanking be enabled
230 * @return 0 on success, or negative value on error.
231 */
is31fl3733_blank(const struct device * dev,bool blank_en)232 int is31fl3733_blank(const struct device *dev, bool blank_en)
233 {
234 const struct is31fl3733_config *config = dev->config;
235 struct is31fl3733_data *data = dev->data;
236 int ret;
237
238 ret = is31fl3733_select_page(dev, CMD_SEL_FUNC);
239 if (ret < 0) {
240 return ret;
241 }
242
243 if (blank_en) {
244 data->conf_reg &= ~CONF_REG_SSD_MASK;
245 } else {
246 data->conf_reg |= CONF_REG_SSD_MASK;
247 }
248
249 return i2c_reg_write_byte_dt(&config->bus, CONF_REG, data->conf_reg);
250 }
251
252 /**
253 * @brief Sets led current limit
254 *
255 * Sets the current limit for the LED driver. This is a separate value
256 * from per-led brightness, and applies to all LEDs.
257 * This value sets the output current limit according
258 * to the following formula: (840/R_ISET) * (limit/256)
259 * See table 14 of the datasheet for additional details.
260 * @param dev: LED device structure
261 * @param limit: current limit to apply
262 * @return 0 on success, or negative value on error.
263 */
is31fl3733_current_limit(const struct device * dev,uint8_t limit)264 int is31fl3733_current_limit(const struct device *dev, uint8_t limit)
265 {
266 const struct is31fl3733_config *config = dev->config;
267 int ret;
268
269 ret = is31fl3733_select_page(dev, CMD_SEL_FUNC);
270 if (ret < 0) {
271 return ret;
272 }
273
274 /* Set global current control register */
275 return i2c_reg_write_byte_dt(&config->bus, GLOBAL_CURRENT_CTRL_REG, limit);
276 }
277
278 static DEVICE_API(led, is31fl3733_api) = {
279 .on = is31fl3733_led_on,
280 .off = is31fl3733_led_off,
281 .set_brightness = is31fl3733_led_set_brightness,
282 .write_channels = is31fl3733_led_write_channels,
283 };
284
285 #define IS31FL3733_DEVICE(n) \
286 static const struct is31fl3733_config is31fl3733_config_##n = { \
287 .bus = I2C_DT_SPEC_INST_GET(n), \
288 .sdb = GPIO_DT_SPEC_INST_GET_OR(n, sdb_gpios, {}), \
289 .current_limit = DT_INST_PROP(n, current_limit), \
290 .sync = DT_INST_ENUM_IDX(n, sync_mode), \
291 }; \
292 \
293 static struct is31fl3733_data is31fl3733_data_##n = { \
294 .selected_page = CMD_SEL_LED, \
295 }; \
296 \
297 DEVICE_DT_INST_DEFINE(n, &is31fl3733_init, NULL, &is31fl3733_data_##n, \
298 &is31fl3733_config_##n, POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \
299 &is31fl3733_api);
300
301 DT_INST_FOREACH_STATUS_OKAY(IS31FL3733_DEVICE)
302