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