1 /*
2 * Copyright (c) 2018 Intel Corporation
3 * Copyright (c) 2019 Nordic Semiconductor ASA
4 * Copyright (c) 2021 Seagate Technology LLC
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #define DT_DRV_COMPAT worldsemi_ws2812_gpio
10
11 #include <drivers/led_strip.h>
12
13 #include <string.h>
14
15 #define LOG_LEVEL CONFIG_LED_STRIP_LOG_LEVEL
16 #include <logging/log.h>
17 LOG_MODULE_REGISTER(ws2812_gpio);
18
19 #include <zephyr.h>
20 #include <soc.h>
21 #include <drivers/gpio.h>
22 #include <device.h>
23 #include <drivers/clock_control.h>
24 #include <drivers/clock_control/nrf_clock_control.h>
25 #include <dt-bindings/led/led.h>
26
27 struct ws2812_gpio_data {
28 const struct device *gpio;
29 };
30
31 struct ws2812_gpio_cfg {
32 uint8_t pin;
33 uint8_t num_colors;
34 const uint8_t *color_mapping;
35 };
36
dev_data(const struct device * dev)37 static struct ws2812_gpio_data *dev_data(const struct device *dev)
38 {
39 return dev->data;
40 }
41
dev_cfg(const struct device * dev)42 static const struct ws2812_gpio_cfg *dev_cfg(const struct device *dev)
43 {
44 return dev->config;
45 }
46
47 /*
48 * This is hard-coded to nRF51 in two ways:
49 *
50 * 1. The assembly delays T1H, T0H, TxL
51 * 2. GPIO set/clear
52 */
53
54 /*
55 * T1H: 1 bit high pulse delay: 12 cycles == .75 usec
56 * T0H: 0 bit high pulse delay: 4 cycles == .25 usec
57 * TxL: inter-bit low pulse delay: 8 cycles == .5 usec
58 *
59 * We can't use k_busy_wait() here: its argument is in microseconds,
60 * and we need roughly .05 microsecond resolution.
61 */
62 #define DELAY_T1H "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
63 #define DELAY_T0H "nop\nnop\nnop\nnop\n"
64 #define DELAY_TxL "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
65
66 /*
67 * GPIO set/clear (these make assumptions about assembly details
68 * below).
69 *
70 * This uses OUTCLR == OUTSET+4.
71 *
72 * We should be able to make this portable using the results of
73 * https://github.com/zephyrproject-rtos/zephyr/issues/11917.
74 *
75 * We already have the GPIO device stashed in ws2812_gpio_data, so
76 * this driver can be used as a test case for the optimized API.
77 *
78 * Per Arm docs, both Rd and Rn must be r0-r7, so we use the "l"
79 * constraint in the below assembly.
80 */
81 #define SET_HIGH "str %[p], [%[r], #0]\n" /* OUTSET = BIT(LED_PIN) */
82 #define SET_LOW "str %[p], [%[r], #4]\n" /* OUTCLR = BIT(LED_PIN) */
83
84 /* Send out a 1 bit's pulse */
85 #define ONE_BIT(base, pin) do { \
86 __asm volatile (SET_HIGH \
87 DELAY_T1H \
88 SET_LOW \
89 DELAY_TxL \
90 :: \
91 [r] "l" (base), \
92 [p] "l" (pin)); } while (0)
93
94 /* Send out a 0 bit's pulse */
95 #define ZERO_BIT(base, pin) do { \
96 __asm volatile (SET_HIGH \
97 DELAY_T0H \
98 SET_LOW \
99 DELAY_TxL \
100 :: \
101 [r] "l" (base), \
102 [p] "l" (pin)); } while (0)
103
send_buf(const struct device * dev,uint8_t * buf,size_t len)104 static int send_buf(const struct device *dev, uint8_t *buf, size_t len)
105 {
106 volatile uint32_t *base = (uint32_t *)&NRF_GPIO->OUTSET;
107 const uint32_t val = BIT(dev_cfg(dev)->pin);
108 struct onoff_manager *mgr =
109 z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
110 struct onoff_client cli;
111 unsigned int key;
112 int rc;
113
114 sys_notify_init_spinwait(&cli.notify);
115 rc = onoff_request(mgr, &cli);
116 if (rc < 0) {
117 return rc;
118 }
119
120 while (sys_notify_fetch_result(&cli.notify, &rc)) {
121 /* pend until clock is up and running */
122 }
123
124 key = irq_lock();
125
126 while (len--) {
127 uint32_t b = *buf++;
128 int32_t i;
129
130 /*
131 * Generate signal out of the bits, MSbit first.
132 *
133 * Accumulator maintenance and branching mean the
134 * inter-bit time will be longer than TxL, but the
135 * wp.josh.com blog post says we have at least 5 usec
136 * of slack time between bits before we risk the
137 * signal getting latched, so this will be fine as
138 * long as the compiler does something minimally
139 * reasonable.
140 */
141 for (i = 7; i >= 0; i--) {
142 if (b & BIT(i)) {
143 ONE_BIT(base, val);
144 } else {
145 ZERO_BIT(base, val);
146 }
147 }
148 }
149
150 irq_unlock(key);
151
152 rc = onoff_release(mgr);
153 /* Returns non-negative value on success. Cap to 0 as API states. */
154 rc = MIN(rc, 0);
155
156 return rc;
157 }
158
ws2812_gpio_update_rgb(const struct device * dev,struct led_rgb * pixels,size_t num_pixels)159 static int ws2812_gpio_update_rgb(const struct device *dev,
160 struct led_rgb *pixels,
161 size_t num_pixels)
162 {
163 const struct ws2812_gpio_cfg *config = dev->config;
164 uint8_t *ptr = (uint8_t *)pixels;
165 size_t i;
166
167 /* Convert from RGB to on-wire format (e.g. GRB, GRBW, RGB, etc) */
168 for (i = 0; i < num_pixels; i++) {
169 uint8_t j;
170
171 for (j = 0; j < config->num_colors; j++) {
172 switch (config->color_mapping[j]) {
173 /* White channel is not supported by LED strip API. */
174 case LED_COLOR_ID_WHITE:
175 *ptr++ = 0;
176 break;
177 case LED_COLOR_ID_RED:
178 *ptr++ = pixels[i].r;
179 break;
180 case LED_COLOR_ID_GREEN:
181 *ptr++ = pixels[i].g;
182 break;
183 case LED_COLOR_ID_BLUE:
184 *ptr++ = pixels[i].b;
185 break;
186 default:
187 return -EINVAL;
188 }
189 }
190 }
191
192 return send_buf(dev, (uint8_t *)pixels, num_pixels * config->num_colors);
193 }
194
ws2812_gpio_update_channels(const struct device * dev,uint8_t * channels,size_t num_channels)195 static int ws2812_gpio_update_channels(const struct device *dev,
196 uint8_t *channels,
197 size_t num_channels)
198 {
199 LOG_ERR("update_channels not implemented");
200 return -ENOTSUP;
201 }
202
203 static const struct led_strip_driver_api ws2812_gpio_api = {
204 .update_rgb = ws2812_gpio_update_rgb,
205 .update_channels = ws2812_gpio_update_channels,
206 };
207
208 #define WS2812_GPIO_DEV(idx) \
209 (DT_INST_GPIO_LABEL(idx, in_gpios))
210 #define WS2812_GPIO_PIN(idx) \
211 (DT_INST_GPIO_PIN(idx, in_gpios))
212 #define WS2812_GPIO_FLAGS(idx) \
213 (DT_INST_GPIO_FLAGS(idx, in_gpios))
214
215 /*
216 * Retrieve the channel to color mapping (e.g. RGB, BGR, GRB, ...) from the
217 * "color-mapping" DT property.
218 */
219 #define WS2812_COLOR_MAPPING(idx) \
220 static const uint8_t ws2812_gpio_##idx##_color_mapping[] = \
221 DT_INST_PROP(idx, color_mapping)
222
223 #define WS2812_NUM_COLORS(idx) (DT_INST_PROP_LEN(idx, color_mapping))
224
225 /*
226 * The inline assembly above is designed to work on nRF51 devices with
227 * the 16 MHz clock enabled.
228 *
229 * TODO: try to make this portable, or at least port to more devices.
230 */
231 #define WS2812_GPIO_CLK(idx) DT_LABEL(DT_INST(0, nordic_nrf_clock))
232
233 #define WS2812_GPIO_DEVICE(idx) \
234 \
235 static int ws2812_gpio_##idx##_init(const struct device *dev) \
236 { \
237 struct ws2812_gpio_data *data = dev_data(dev); \
238 const struct ws2812_gpio_cfg *cfg = dev_cfg(dev); \
239 uint8_t i; \
240 \
241 data->gpio = device_get_binding(WS2812_GPIO_DEV(idx)); \
242 if (!data->gpio) { \
243 LOG_ERR("Unable to find GPIO controller %s", \
244 WS2812_GPIO_DEV(idx)); \
245 return -ENODEV; \
246 } \
247 \
248 for (i = 0; i < cfg->num_colors; i++) { \
249 switch (cfg->color_mapping[i]) { \
250 case LED_COLOR_ID_WHITE: \
251 case LED_COLOR_ID_RED: \
252 case LED_COLOR_ID_GREEN: \
253 case LED_COLOR_ID_BLUE: \
254 break; \
255 default: \
256 LOG_ERR("%s: invalid channel to color mapping." \
257 " Check the color-mapping DT property", \
258 dev->name); \
259 return -EINVAL; \
260 } \
261 } \
262 \
263 return gpio_pin_configure(data->gpio, \
264 WS2812_GPIO_PIN(idx), \
265 WS2812_GPIO_FLAGS(idx) | \
266 GPIO_OUTPUT); \
267 } \
268 \
269 static struct ws2812_gpio_data ws2812_gpio_##idx##_data; \
270 \
271 WS2812_COLOR_MAPPING(idx); \
272 \
273 static const struct ws2812_gpio_cfg ws2812_gpio_##idx##_cfg = { \
274 .pin = WS2812_GPIO_PIN(idx), \
275 .num_colors = WS2812_NUM_COLORS(idx), \
276 .color_mapping = ws2812_gpio_##idx##_color_mapping, \
277 }; \
278 \
279 DEVICE_DT_INST_DEFINE(idx, \
280 ws2812_gpio_##idx##_init, \
281 NULL, \
282 &ws2812_gpio_##idx##_data, \
283 &ws2812_gpio_##idx##_cfg, POST_KERNEL, \
284 CONFIG_LED_STRIP_INIT_PRIORITY, \
285 &ws2812_gpio_api);
286
287 DT_INST_FOREACH_STATUS_OKAY(WS2812_GPIO_DEVICE)
288