blob: c8501156e8143619c0bb1972c1c90a0546330ee5 [file] [log] [blame]
Javad Rahimipetroudi9d15f662024-02-06 10:41:53 +01001/*
2 * Copyright (c) 2024 Javad Rahimipetroudi <javad.rahimipetroudi@mind.be>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#define DT_DRV_COMPAT ti_tlc59731
8
9/**
10 * @file
11 * @brief LED driver for the TLC59731 LED driver.
12 *
13 * TLC59731 is a 3-Channel, 8-Bit, PWM LED Driver
14 * With Single-Wire Interface (EasySet)
15 *
16 * The EasySet protocol is based on short pulses and the time between
17 * them. At least one pulse must be sent every T_CYCLE, which can be
18 * between 1.67us and 50us. We want to go as fast as possible, but
19 * delays under 1us don't work very well, so we settle on 5us for the
20 * cycle time.
21 * A pulse must be high for at least 14ns. In practice, turning a GPIO on
22 * and immediately off again already takes longer than that, so no delay
23 * is needed there.
24 * A zero is represented by no additional pulses within a cycle.
25 * A one is represented by an additional pulse between 275ns and 2.5us
26 * (half a cycle) after the first one. We need at least some delay to get to
27 * 275ns, but because of the limited granularity of k_busy_wait we use a
28 * full 1us. After the pulse, we wait an additional T_CYCLE_1 to complete
29 * the cycle. This time can be slightly shorter because the second pulse
30 * already closes the cycle.
31 * Finally we need to keep the line low for T_H0 to complete the address
32 * for a single chip, and T_H1 to complete the write for all chips.
33 */
34
35#include <zephyr/drivers/led_strip.h>
36#include <zephyr/drivers/gpio.h>
37#include <zephyr/device.h>
38#include <zephyr/kernel.h>
39
40#include <zephyr/logging/log.h>
41LOG_MODULE_REGISTER(tlc59731, CONFIG_LED_STRIP_LOG_LEVEL);
42
43/* Pulse timing */
44#define TLC59731_DELAY 0x01 /* us */
45#define TLC59731_T_CYCLE_0 0x04 /* us */
46#define TLC59731_T_CYCLE_1 0x01 /* us */
47#define TLC59731_T_H0 (4 * TLC59731_T_CYCLE_0)
48#define TLC59731_T_H1 (8 * TLC59731_T_CYCLE_0)
49/* Threshould levels */
50#define TLC59731_HIGH 0x01
51#define TLC59731_LOW 0x00
52
53/* Write command */
54#define TLC59731_WR 0x3A
55
56struct tlc59731_cfg {
57 struct gpio_dt_spec sdi_gpio;
58 size_t length;
59};
60
61static inline int rgb_pulse(const struct gpio_dt_spec *led_dev)
62{
63 int fret = 0;
64
65 fret = gpio_pin_set_dt(led_dev, TLC59731_HIGH);
66 if (fret != 0) {
67 return fret;
68 }
69
70 fret = gpio_pin_set_dt(led_dev, TLC59731_LOW);
71 if (fret != 0) {
72 return fret;
73 }
74
75 return fret;
76}
77
78static int rgb_write_bit(const struct gpio_dt_spec *led_dev, uint8_t data)
79{
80 rgb_pulse(led_dev);
81
82 k_busy_wait(TLC59731_DELAY);
83
84 if (data) {
85 rgb_pulse(led_dev);
86 k_busy_wait(TLC59731_T_CYCLE_1);
87 } else {
88 k_busy_wait(TLC59731_T_CYCLE_0);
89 }
90
91 return 0;
92}
93
94static int rgb_write_data(const struct gpio_dt_spec *led_dev, uint8_t data)
95{
96 int8_t idx = 7;
97
98 while (idx >= 0) {
99 rgb_write_bit(led_dev, data & BIT((idx--)));
100 }
101
102 return 0;
103}
104
105static int tlc59731_led_set_color(const struct device *dev, struct led_rgb *pixel)
106{
107
108 const struct tlc59731_cfg *tlc_conf = dev->config;
109 const struct gpio_dt_spec *led_gpio = &tlc_conf->sdi_gpio;
110
111 rgb_write_data(led_gpio, TLC59731_WR);
112 rgb_write_data(led_gpio, pixel->r);
113 rgb_write_data(led_gpio, pixel->g);
114 rgb_write_data(led_gpio, pixel->b);
115
116 return 0;
117}
118
119static int tlc59731_gpio_update_rgb(const struct device *dev, struct led_rgb *pixels,
120 size_t num_pixels)
121{
122 size_t i;
123 int err = 0;
124
125 for (i = 0; i < num_pixels; i++) {
126 err = tlc59731_led_set_color(dev, &pixels[i]);
127 if (err) {
128 break;
129 }
130 }
131
132 return err;
133}
134
135static size_t tlc59731_length(const struct device *dev)
136{
137 const struct tlc59731_cfg *config = dev->config;
138
139 return config->length;
140}
141
142static const struct led_strip_driver_api tlc59731_gpio_api = {
143 .update_rgb = tlc59731_gpio_update_rgb,
144 .length = tlc59731_length,
145};
146
147static int tlc59731_gpio_init(const struct device *dev)
148{
149 const struct tlc59731_cfg *tlc_conf = dev->config;
150 const struct gpio_dt_spec *led = &tlc_conf->sdi_gpio;
151 int err = 0;
152
153 if (!device_is_ready(led->port)) {
154 LOG_ERR("%s: no LEDs found (DT child nodes missing)", dev->name);
155 err = -ENODEV;
156 goto scape;
157 }
158
159 err = gpio_pin_configure_dt(led, GPIO_OUTPUT_ACTIVE);
160 if (err < 0) {
161 LOG_ERR("%s: Unable to setup SDI port", dev->name);
162 err = -EIO;
163 goto scape;
164 }
165
166 err = gpio_pin_set_dt(led, TLC59731_LOW);
167 if (err < 0) {
168 LOG_ERR("%s: Unable to set the SDI-GPIO)", dev->name);
169 err = -EIO;
170 goto scape;
171 }
172
173 gpio_pin_set_dt(led, TLC59731_HIGH);
174 gpio_pin_set_dt(led, TLC59731_LOW);
175
176 k_busy_wait((TLC59731_DELAY + TLC59731_T_CYCLE_0));
177scape:
178 return err;
179}
180
181#define TLC59731_DEVICE(i) \
182 static struct tlc59731_cfg tlc59731_cfg_##i = { \
183 .sdi_gpio = GPIO_DT_SPEC_INST_GET(i, gpios), \
184 .length = DT_INST_PROP(i, chain_length), \
185 }; \
186 \
187 DEVICE_DT_INST_DEFINE(i, tlc59731_gpio_init, NULL, NULL, &tlc59731_cfg_##i, POST_KERNEL, \
188 CONFIG_LED_STRIP_INIT_PRIORITY, &tlc59731_gpio_api);
189DT_INST_FOREACH_STATUS_OKAY(TLC59731_DEVICE)