1 /*
2  * Copyright (c) 2018 Google LLC.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT apa_apa102
8 
9 #include <errno.h>
10 #include <zephyr/drivers/led_strip.h>
11 #include <zephyr/drivers/spi.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/sys/util.h>
14 
15 struct apa102_config {
16 	struct spi_dt_spec bus;
17 	size_t length;
18 };
19 
apa102_update(const struct device * dev,void * buf,size_t size)20 static int apa102_update(const struct device *dev, void *buf, size_t size)
21 {
22 	const struct apa102_config *config = dev->config;
23 	static const uint8_t zeros[] = { 0, 0, 0, 0 };
24 	static const uint8_t ones[] = { 0xFF, 0xFF, 0xFF, 0xFF };
25 	const struct spi_buf tx_bufs[] = {
26 		{
27 			/* Start frame: at least 32 zeros */
28 			.buf = (uint8_t *)zeros,
29 			.len = sizeof(zeros),
30 		},
31 		{
32 			/* LED data itself */
33 			.buf = buf,
34 			.len = size,
35 		},
36 		{
37 			/* End frame: at least 32 ones to clock the
38 			 * remaining bits to the LEDs at the end of
39 			 * the strip.
40 			 */
41 			.buf = (uint8_t *)ones,
42 			.len = sizeof(ones),
43 		},
44 	};
45 	const struct spi_buf_set tx = {
46 		.buffers = tx_bufs,
47 		.count = ARRAY_SIZE(tx_bufs)
48 	};
49 
50 	return spi_write_dt(&config->bus, &tx);
51 }
52 
apa102_update_rgb(const struct device * dev,struct led_rgb * pixels,size_t count)53 static int apa102_update_rgb(const struct device *dev, struct led_rgb *pixels,
54 			     size_t count)
55 {
56 	uint8_t *p = (uint8_t *)pixels;
57 	size_t i;
58 	/* SOF (3 bits) followed by the 0 to 31 global dimming level */
59 	uint8_t prefix = 0xE0 | 31;
60 
61 	/* Rewrite to the on-wire format */
62 	for (i = 0; i < count; i++) {
63 		uint8_t r = pixels[i].r;
64 		uint8_t g = pixels[i].g;
65 		uint8_t b = pixels[i].b;
66 
67 		*p++ = prefix;
68 		*p++ = b;
69 		*p++ = g;
70 		*p++ = r;
71 	}
72 
73 	BUILD_ASSERT(sizeof(struct led_rgb) == 4);
74 	return apa102_update(dev, pixels, sizeof(struct led_rgb) * count);
75 }
76 
apa102_length(const struct device * dev)77 static size_t apa102_length(const struct device *dev)
78 {
79 	const struct apa102_config *config = dev->config;
80 
81 	return config->length;
82 }
83 
apa102_init(const struct device * dev)84 static int apa102_init(const struct device *dev)
85 {
86 	const struct apa102_config *config = dev->config;
87 
88 	if (!spi_is_ready_dt(&config->bus)) {
89 		return -ENODEV;
90 	}
91 
92 	return 0;
93 }
94 
95 static DEVICE_API(led_strip, apa102_api) = {
96 	.update_rgb = apa102_update_rgb,
97 	.length = apa102_length,
98 };
99 
100 #define APA102_DEVICE(idx)						 \
101 	static const struct apa102_config apa102_##idx##_config = {	 \
102 		.bus = SPI_DT_SPEC_INST_GET(				 \
103 			idx,						 \
104 			SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), \
105 			0),						 \
106 		.length = DT_INST_PROP(idx, chain_length),		 \
107 	};								 \
108 									 \
109 	DEVICE_DT_INST_DEFINE(idx,					 \
110 			      apa102_init,				 \
111 			      NULL,					 \
112 			      NULL,					 \
113 			      &apa102_##idx##_config,			 \
114 			      POST_KERNEL,				 \
115 			      CONFIG_LED_STRIP_INIT_PRIORITY,		 \
116 			      &apa102_api);
117 
118 DT_INST_FOREACH_STATUS_OKAY(APA102_DEVICE)
119