1 /*
2  * Copyright (c) 2022 Esco Medical ApS
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_tlc5971
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/spi.h>
11 #include <zephyr/drivers/led_strip.h>
12 #include <zephyr/drivers/led_strip/tlc5971.h>
13 #include <zephyr/dt-bindings/led/led.h>
14 #include <zephyr/sys/util.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(tlc5971, CONFIG_LED_STRIP_LOG_LEVEL);
18 
19 struct tlc5971_config {
20 	struct spi_dt_spec bus;
21 	const uint8_t *color_mapping;
22 	uint8_t num_pixels;
23 	uint8_t num_colors;
24 };
25 
26 struct tlc5971_data {
27 	uint8_t *data_buffer;
28 	uint8_t gbc_color_1;
29 	uint8_t gbc_color_2;
30 	uint8_t gbc_color_3;
31 	uint8_t control_data;
32 };
33 
34 /** SPI operation word constant, SPI mode 0, CPOL = 0, CPHA = 0 */
35 #define TLC5971_SPI_OPERATION (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8))
36 
37 /** Number of supported colors */
38 #define TLC5971_NUMBER_OF_COLORS 3
39 
40 /**
41  * @brief Number of RGB pixels per TLC5791 device
42  *
43  * The TLC5971 has 4x RGB outputs per device, where each RGB group constitues a pixel from this
44  * drivers point of view.
45  */
46 #define TLC5971_PIXELS_PER_DEVICE 4
47 
48 /** Length in bytes of data packet per TLC5791 device */
49 #define TLC5971_PACKET_LEN 28
50 
51 /** write command for writing control data and GS data to internal registers */
52 #define TLC5971_WRITE_COMMAND 0x25
53 
54 /** GS reference clock select bit in FC data (0 = internal oscillator clock, 1 = SCKI clock). */
55 #define TLC5971_BYTE27_CTRL_BIT_EXTGCK BIT(0)
56 
57 /** GS reference clock edge select bit for OUTXn on-off timing control in FC data */
58 #define TLC5971_BYTE27_CTRL_BIT_OUTTMG BIT(1)
59 
60 /** Constant-current output enable bit in FC data (0 = output control enabled, 1 = blank). */
61 #define TLC5971_BYTE26_CTRL_BIT_BLANK BIT(5)
62 
63 /** Auto display repeat mode enable bit in FC data (0 = disabled, 1 = enabled). */
64 #define TLC5971_BYTE26_CTRL_BIT_DSPRPT BIT(6)
65 
66 /** Display timing reset mode enable bit in FC data (0 = disabled, 1 = enabled). */
67 #define TLC5971_BYTE26_CTRL_BIT_TMGRST BIT(7)
68 
69 /** Bit mask for write cmd in data byte 27 */
70 #define TLC5971_BYTE27_WRITE_CMD_MASK GENMASK(7, 2)
71 
72 /** Bit mask for control bits in data byte 27 */
73 #define TLC5971_BYTE27_CTRL_MASK GENMASK(1, 0)
74 
75 /** Bit mask for control bits in data byte 26 */
76 #define TLC5971_BYTE26_CTRL_MASK GENMASK(7, 5)
77 
78 /** Bit mask for global brightness control for color 1 in data byte 26, upper 5 bits of GBC */
79 #define TLC5971_BYTE26_GBC1_MASK GENMASK(4, 0)
80 
81 /** Bit mask for global brightness control for color 1 in data byte 25, lower 2 bits of GBC */
82 #define TLC5971_BYTE25_GBC1_MASK GENMASK(7, 6)
83 
84 /** Bit mask for global brightness control for color 2 in data byte 25, upper 6 bits of GBC */
85 #define TLC5971_BYTE25_GBC2_MASK GENMASK(5, 0)
86 
87 /** Bit mask for global brightness control for color 2 in data byte 24, lower 1 bits of GBC */
88 #define TLC5971_BYTE24_GBC2_MASK BIT(7)
89 
90 /** Bit mask for global brightness control for color 3 in data byte 24, all 7 bits of GBC */
91 #define TLC5971_BYTE24_GBC3_MASK GENMASK(6, 0)
92 
93 /**
94  * @brief create data byte 27 from control data
95  *
96  * @param control_data control bits
97  * @return uint8_t the serialized data byte 27
98  */
tlc5971_data_byte27(uint8_t control_data)99 static inline uint8_t tlc5971_data_byte27(uint8_t control_data)
100 {
101 	return FIELD_PREP(TLC5971_BYTE27_WRITE_CMD_MASK, TLC5971_WRITE_COMMAND) |
102 	       FIELD_PREP(TLC5971_BYTE27_CTRL_MASK, control_data);
103 }
104 
105 /**
106  * @brief create data byte 26 from control data and color 1 GBC
107  *
108  * @param control_data control bits
109  * @param gbc_color_1 global brightness control for color 1 LEDs
110  * @return uint8_t the serialized data byte 26
111  */
tlc5971_data_byte26(uint8_t control_data,uint8_t gbc_color_1)112 static inline uint8_t tlc5971_data_byte26(uint8_t control_data, uint8_t gbc_color_1)
113 {
114 	return FIELD_PREP(TLC5971_BYTE26_CTRL_MASK, control_data) |
115 	       FIELD_PREP(TLC5971_BYTE26_GBC1_MASK, (gbc_color_1 >> 2));
116 }
117 
118 /**
119  * @brief create data byte 25 from color 1 and 2 GBC
120  *
121  * @param gbc_color_1 global brightness control for color 1 LEDs
122  * @param gbc_color_2 global brightness control for color 2 LEDs
123  * @return uint8_t the serialized data byte 25
124  */
tlc5971_data_byte25(uint8_t gbc_color_1,uint8_t gbc_color_2)125 static inline uint8_t tlc5971_data_byte25(uint8_t gbc_color_1, uint8_t gbc_color_2)
126 {
127 	return FIELD_PREP(TLC5971_BYTE25_GBC1_MASK, gbc_color_1) |
128 	       FIELD_PREP(TLC5971_BYTE25_GBC2_MASK, (gbc_color_2 >> 1));
129 }
130 
131 /**
132  * @brief create data byte 24 from color 2 and 3 GBC
133  *
134  * @param gbc_color_2 global brightness control for color 2 LEDs
135  * @param gbc_color_3 global brightness control for color 3 LEDs
136  * @return uint8_t the serialized data byte 24
137  */
tlc5971_data_byte24(uint8_t gbc_color_2,uint8_t gbc_color_3)138 static inline uint8_t tlc5971_data_byte24(uint8_t gbc_color_2, uint8_t gbc_color_3)
139 {
140 	return FIELD_PREP(TLC5971_BYTE24_GBC2_MASK, gbc_color_2) |
141 	       FIELD_PREP(TLC5971_BYTE24_GBC3_MASK, gbc_color_3);
142 }
143 
144 /**
145  * @brief map user colors to tlc5971 color order
146  *
147  * @param color_id color id from color mapping
148  * @param pixel_data rgb data to be mapped
149  * @return the mapped color value
150  */
tlc5971_map_color(int color_id,const struct led_rgb * pixel_data)151 static uint8_t tlc5971_map_color(int color_id, const struct led_rgb *pixel_data)
152 {
153 	uint8_t temp = 0;
154 
155 	switch (color_id) {
156 	case LED_COLOR_ID_RED:
157 		temp = pixel_data->r;
158 		break;
159 	case LED_COLOR_ID_GREEN:
160 		temp = pixel_data->g;
161 		break;
162 	case LED_COLOR_ID_BLUE:
163 		temp = pixel_data->b;
164 		break;
165 	default:
166 		temp = 0;
167 		break;
168 	}
169 
170 	return temp;
171 }
172 
173 /**
174  * @brief serialize control data and pixel data for device daisy chain
175  *
176  * the serializer only supports "full" devices, meaning each device is expected
177  * to be mounted with all 4 LEDs.
178  *
179  * @param dev device pointer
180  * @param pixels pixel RGB data for daisy chain
181  * @param num_pixels number of pixels in daisy chain
182  */
tlc5971_fill_data_buffer(const struct device * dev,struct led_rgb * pixels,size_t num_pixels)183 static void tlc5971_fill_data_buffer(const struct device *dev, struct led_rgb *pixels,
184 				     size_t num_pixels)
185 {
186 	const struct tlc5971_config *cfg = dev->config;
187 	struct tlc5971_data *data = dev->data;
188 	uint8_t *data_buffer = data->data_buffer;
189 	int count = 0;
190 
191 	/*
192 	 * tlc5971 device order is reversed as the rgb data for the last device in the daisy chain
193 	 * should be transmitted first.
194 	 */
195 	for (int device = (num_pixels / TLC5971_PIXELS_PER_DEVICE) - 1; device >= 0; device--) {
196 		/*
197 		 * The SPI frame format expects a BGR color order for the global brightness control
198 		 * values, but since the led_strip API allows custom color mappings, we simply use
199 		 * color_x terms to keep things generic.
200 		 */
201 		data_buffer[count++] = tlc5971_data_byte27(data->control_data);
202 		data_buffer[count++] = tlc5971_data_byte26(data->control_data, data->gbc_color_1);
203 		data_buffer[count++] = tlc5971_data_byte25(data->gbc_color_1, data->gbc_color_2);
204 		data_buffer[count++] = tlc5971_data_byte24(data->gbc_color_2, data->gbc_color_3);
205 
206 		for (int pixel = (TLC5971_PIXELS_PER_DEVICE - 1); pixel >= 0; pixel--) {
207 			/* data is "reversed" so RGB0 comes last, i.e at byte 0 */
208 			const struct led_rgb *pixel_data =
209 				&pixels[(device * TLC5971_PIXELS_PER_DEVICE) + pixel];
210 
211 			/*
212 			 * Convert pixel data into SPI frames, mapping user colors to tlc5971
213 			 * data frame color order (BGR).
214 			 */
215 			for (int color = 0; color < cfg->num_colors; color++) {
216 				uint8_t temp =
217 					tlc5971_map_color(cfg->color_mapping[color], pixel_data);
218 
219 				/*
220 				 * The tlc5971 rgb values are 16 bit but zephyr's rgb values are
221 				 * 8 bit. Simply upscale to 16 bit by using the 8 bit value for both
222 				 * LSB and MSB of the 16 bit word.
223 				 */
224 				data_buffer[count++] = temp;
225 				data_buffer[count++] = temp;
226 			}
227 		}
228 	}
229 }
230 
tlc5971_transmit_data(const struct device * dev,size_t num_pixels)231 static int tlc5971_transmit_data(const struct device *dev, size_t num_pixels)
232 {
233 	const struct tlc5971_config *cfg = dev->config;
234 	struct tlc5971_data *data = dev->data;
235 
236 	struct spi_buf buf = {
237 		.buf = data->data_buffer,
238 		.len = (num_pixels / TLC5971_PIXELS_PER_DEVICE) * TLC5971_PACKET_LEN,
239 	};
240 
241 	const struct spi_buf_set tx = {
242 		.buffers = &buf,
243 		.count = 1,
244 	};
245 
246 	return spi_write_dt(&cfg->bus, &tx);
247 }
248 
tlc5971_update_rgb(const struct device * dev,struct led_rgb * pixels,size_t num_pixels)249 static int tlc5971_update_rgb(const struct device *dev, struct led_rgb *pixels, size_t num_pixels)
250 {
251 	tlc5971_fill_data_buffer(dev, pixels, num_pixels);
252 
253 	return tlc5971_transmit_data(dev, num_pixels);
254 }
255 
tlc5971_length(const struct device * dev)256 static size_t tlc5971_length(const struct device *dev)
257 {
258 	const struct tlc5971_config *cfg = dev->config;
259 
260 	return (size_t)cfg->num_pixels;
261 }
262 
tlc5971_set_global_brightness(const struct device * dev,struct led_rgb pixel)263 int tlc5971_set_global_brightness(const struct device *dev, struct led_rgb pixel)
264 {
265 	const struct tlc5971_config *cfg = dev->config;
266 	struct tlc5971_data *data = dev->data;
267 	int res = -EINVAL;
268 
269 	if ((pixel.r <= TLC5971_GLOBAL_BRIGHTNESS_CONTROL_MAX) &&
270 	    (pixel.g <= TLC5971_GLOBAL_BRIGHTNESS_CONTROL_MAX) &&
271 	    (pixel.b <= TLC5971_GLOBAL_BRIGHTNESS_CONTROL_MAX)) {
272 		data->gbc_color_1 = tlc5971_map_color(cfg->color_mapping[0], &pixel);
273 		data->gbc_color_2 = tlc5971_map_color(cfg->color_mapping[1], &pixel);
274 		data->gbc_color_3 = tlc5971_map_color(cfg->color_mapping[2], &pixel);
275 		res = 0;
276 	}
277 
278 	return res;
279 }
280 
tlc5971_init(const struct device * dev)281 static int tlc5971_init(const struct device *dev)
282 {
283 	const struct tlc5971_config *cfg = dev->config;
284 	struct tlc5971_data *data = dev->data;
285 
286 	if (!spi_is_ready_dt(&cfg->bus)) {
287 		LOG_ERR("%s: SPI device %s not ready", dev->name, cfg->bus.bus->name);
288 		return -ENODEV;
289 	}
290 
291 	if ((cfg->num_pixels % TLC5971_PIXELS_PER_DEVICE) != 0) {
292 		LOG_ERR("%s: chain length must be multiple of 4", dev->name);
293 		return -EINVAL;
294 	}
295 
296 	if (cfg->num_colors != TLC5971_NUMBER_OF_COLORS) {
297 		LOG_ERR("%s: the tlc5971 only supports %i colors", dev->name,
298 			TLC5971_NUMBER_OF_COLORS);
299 		return -EINVAL;
300 	}
301 
302 	for (int i = 0; i < cfg->num_colors; i++) {
303 		switch (cfg->color_mapping[i]) {
304 		case LED_COLOR_ID_RED:
305 		case LED_COLOR_ID_GREEN:
306 		case LED_COLOR_ID_BLUE:
307 			break;
308 		default:
309 			LOG_ERR("%s: invalid color mapping", dev->name);
310 			return -EINVAL;
311 		}
312 	}
313 
314 	/*
315 	 * set up sane defaults for control data.
316 	 * unblanks leds, enables auto display repeat, enables timing resetm uses internal clock for
317 	 * PWM generation and sets the GS reference clock edge select to rising edge
318 	 */
319 	data->control_data = TLC5971_BYTE27_CTRL_BIT_OUTTMG | TLC5971_BYTE26_CTRL_BIT_DSPRPT |
320 			     TLC5971_BYTE26_CTRL_BIT_TMGRST;
321 
322 	return 0;
323 }
324 
325 static DEVICE_API(led_strip, tlc5971_api) = {
326 	.update_rgb = tlc5971_update_rgb,
327 	.length = tlc5971_length,
328 };
329 
330 #define TLC5971_DATA_BUFFER_LENGTH(inst)                                                           \
331 	(DT_INST_PROP(inst, chain_length) / TLC5971_PIXELS_PER_DEVICE) * TLC5971_PACKET_LEN
332 
333 #define TLC5971_DEVICE(inst)                                                                       \
334 	static const uint8_t tlc5971_##inst##_color_mapping[] = DT_INST_PROP(inst, color_mapping); \
335 	static const struct tlc5971_config tlc5971_##inst##_config = {                             \
336 		.bus = SPI_DT_SPEC_INST_GET(inst, TLC5971_SPI_OPERATION, 0),                       \
337 		.num_pixels = DT_INST_PROP(inst, chain_length),                                    \
338 		.num_colors = DT_INST_PROP_LEN(inst, color_mapping),                               \
339 		.color_mapping = tlc5971_##inst##_color_mapping,                                   \
340 	};                                                                                         \
341 	static uint8_t tlc5971_##inst##_data_buffer[TLC5971_DATA_BUFFER_LENGTH(inst)];             \
342 	static struct tlc5971_data tlc5971_##inst##_data = {                                       \
343 		.data_buffer = tlc5971_##inst##_data_buffer,                                       \
344 		.gbc_color_1 = TLC5971_GLOBAL_BRIGHTNESS_CONTROL_MAX,                              \
345 		.gbc_color_2 = TLC5971_GLOBAL_BRIGHTNESS_CONTROL_MAX,                              \
346 		.gbc_color_3 = TLC5971_GLOBAL_BRIGHTNESS_CONTROL_MAX,                              \
347 	};                                                                                         \
348 	DEVICE_DT_INST_DEFINE(inst, tlc5971_init, NULL, &tlc5971_##inst##_data,                    \
349 			      &tlc5971_##inst##_config, POST_KERNEL,                               \
350 			      CONFIG_LED_STRIP_INIT_PRIORITY, &tlc5971_api);
351 
352 DT_INST_FOREACH_STATUS_OKAY(TLC5971_DEVICE)
353