1 // SPDX-License-Identifier: GPL-2.0-only
3 #include <linux/delay.h>
5 #include <linux/init.h>
6 #include <linux/leds.h>
7 #include <linux/module.h>
8 #include <linux/mutex.h>
10 #include <linux/of_gpio.h>
11 #include <linux/gpio/consumer.h>
14 * Driver for the Ubiquiti RGB LED controller (LEDBAR).
15 * This Controller is based on a Holtek HT32F52241 and connected
18 * - The Controller needs an enable signal set to high when
19 * performing a transaction. On the U6-LR, this is located
22 * - The Pin is also printed when calling the "usetled" function
23 * contained in the ubntapp bootloader application.
26 #define UBNT_LEDBAR_MAX_BRIGHTNESS 0xff
28 #define UBNT_LEDBAR_TRANSACTION_LENGTH 8
29 #define UBNT_LEDBAR_TRANSACTION_SUCCESS (char) 0xaa
31 #define UBNT_LEDBAR_TRANSACTION_BLUE_IDX 2
32 #define UBNT_LEDBAR_TRANSACTION_GREEN_IDX 3
33 #define UBNT_LEDBAR_TRANSACTION_RED_IDX 4
34 #define UBNT_LEDBAR_TRANSACTION_LED_COUNT_IDX 6
39 struct i2c_client
*client
;
40 struct led_classdev led_red
;
41 struct led_classdev led_green
;
42 struct led_classdev led_blue
;
43 struct gpio_desc
*enable_gpio
;
44 struct gpio_desc
*reset_gpio
;
47 static void ubnt_ledbar_perform_transaction(struct ubnt_ledbar
*ledbar
,
48 const char *transaction
, int len
,
49 char *result
, int result_len
)
53 for (i
= 0; i
< len
; i
++)
54 i2c_smbus_write_byte(ledbar
->client
, transaction
[i
]);
56 for (i
= 0; i
< result_len
; i
++)
57 result
[i
] = i2c_smbus_read_byte(ledbar
->client
);
60 static int ubnt_ledbar_apply_state(struct ubnt_ledbar
*ledbar
)
62 char setup_msg
[UBNT_LEDBAR_TRANSACTION_LENGTH
] = {0x40, 0x10, 0x00, 0x00,
63 0x00, 0x00, 0x00, 0x11};
64 char led_msg
[UBNT_LEDBAR_TRANSACTION_LENGTH
] = {0x40, 0x00, 0x00, 0x00,
65 0x00, 0x00, 0x00, 0x00};
69 mutex_lock(&ledbar
->lock
);
71 led_msg
[UBNT_LEDBAR_TRANSACTION_BLUE_IDX
] = ledbar
->led_blue
.brightness
;
72 led_msg
[UBNT_LEDBAR_TRANSACTION_GREEN_IDX
] = ledbar
->led_green
.brightness
;
73 led_msg
[UBNT_LEDBAR_TRANSACTION_RED_IDX
] = ledbar
->led_red
.brightness
;
74 led_msg
[UBNT_LEDBAR_TRANSACTION_LED_COUNT_IDX
] = ledbar
->led_count
;
76 gpiod_set_value(ledbar
->enable_gpio
, 1);
80 ubnt_ledbar_perform_transaction(ledbar
, setup_msg
, sizeof(setup_msg
), &i2c_response
, sizeof(i2c_response
));
81 if (i2c_response
!= UBNT_LEDBAR_TRANSACTION_SUCCESS
) {
82 dev_err(&ledbar
->client
->dev
, "Error initializing LED transaction: %02hhx\n", i2c_response
);
87 ubnt_ledbar_perform_transaction(ledbar
, led_msg
, sizeof(led_msg
), &i2c_response
, sizeof(i2c_response
));
88 if (i2c_response
!= UBNT_LEDBAR_TRANSACTION_SUCCESS
) {
89 dev_err(&ledbar
->client
->dev
, "Failed LED transaction: %02hhx\n", i2c_response
);
96 gpiod_set_value(ledbar
->enable_gpio
, 0);
98 mutex_unlock(&ledbar
->lock
);
103 static void ubnt_ledbar_reset(struct ubnt_ledbar
*ledbar
)
105 static const char init_msg
[16] = {0x02, 0x81, 0xfd, 0x7e,
106 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x00};
109 char init_response
[4];
111 if (!ledbar
->reset_gpio
)
114 mutex_lock(&ledbar
->lock
);
116 gpiod_set_value(ledbar
->reset_gpio
, 1);
118 gpiod_set_value(ledbar
->reset_gpio
, 0);
122 gpiod_set_value(ledbar
->enable_gpio
, 1);
124 ubnt_ledbar_perform_transaction(ledbar
, init_msg
, sizeof(init_msg
), init_response
, sizeof(init_response
));
126 gpiod_set_value(ledbar
->enable_gpio
, 0);
128 mutex_unlock(&ledbar
->lock
);
131 #define UBNT_LEDBAR_CONTROL_RGBS(name) \
132 static int ubnt_ledbar_set_##name##_brightness(struct led_classdev *led_cdev,\
133 enum led_brightness value) \
135 struct ubnt_ledbar *ledbar = \
136 container_of(led_cdev, struct ubnt_ledbar, led_##name); \
138 led_cdev->brightness = value; \
139 ret = ubnt_ledbar_apply_state(ledbar); \
143 UBNT_LEDBAR_CONTROL_RGBS(red
);
144 UBNT_LEDBAR_CONTROL_RGBS(green
);
145 UBNT_LEDBAR_CONTROL_RGBS(blue
);
148 static int ubnt_ledbar_init_led(struct device_node
*np
, struct ubnt_ledbar
*ledbar
,
149 struct led_classdev
*led_cdev
)
151 struct led_init_data init_data
= {};
157 init_data
.fwnode
= of_fwnode_handle(np
);
159 led_cdev
->max_brightness
= UBNT_LEDBAR_MAX_BRIGHTNESS
;
161 ret
= devm_led_classdev_register_ext(&ledbar
->client
->dev
, led_cdev
,
164 dev_err(&ledbar
->client
->dev
, "led register err: %d\n", ret
);
170 static int ubnt_ledbar_probe(struct i2c_client
*client
,
171 const struct i2c_device_id
*id
)
173 struct device_node
*np
= client
->dev
.of_node
;
174 struct ubnt_ledbar
*ledbar
;
177 ledbar
= devm_kzalloc(&client
->dev
, sizeof(*ledbar
), GFP_KERNEL
);
181 ledbar
->enable_gpio
= devm_gpiod_get(&client
->dev
, "enable", GPIOD_OUT_LOW
);
183 if (IS_ERR(ledbar
->enable_gpio
)) {
184 ret
= PTR_ERR(ledbar
->enable_gpio
);
185 dev_err(&client
->dev
, "Failed to get enable gpio: %d\n", ret
);
189 ledbar
->reset_gpio
= devm_gpiod_get_optional(&client
->dev
, "reset", GPIOD_OUT_LOW
);
191 if (IS_ERR(ledbar
->reset_gpio
)) {
192 ret
= PTR_ERR(ledbar
->reset_gpio
);
193 dev_err(&client
->dev
, "Failed to get reset gpio: %d\n", ret
);
197 ledbar
->led_count
= 1;
198 of_property_read_u32(np
, "led-count", &ledbar
->led_count
);
200 ledbar
->client
= client
;
202 mutex_init(&ledbar
->lock
);
204 i2c_set_clientdata(client
, ledbar
);
206 // Reset and initialize the MCU
207 ubnt_ledbar_reset(ledbar
);
209 ledbar
->led_red
.brightness_set_blocking
= ubnt_ledbar_set_red_brightness
;
210 ubnt_ledbar_init_led(of_get_child_by_name(np
, "red"), ledbar
, &ledbar
->led_red
);
212 ledbar
->led_green
.brightness_set_blocking
= ubnt_ledbar_set_green_brightness
;
213 ubnt_ledbar_init_led(of_get_child_by_name(np
, "green"), ledbar
, &ledbar
->led_green
);
215 ledbar
->led_blue
.brightness_set_blocking
= ubnt_ledbar_set_blue_brightness
;
216 ubnt_ledbar_init_led(of_get_child_by_name(np
, "blue"), ledbar
, &ledbar
->led_blue
);
218 return ubnt_ledbar_apply_state(ledbar
);
221 static int ubnt_ledbar_remove(struct i2c_client
*client
)
223 struct ubnt_ledbar
*ledbar
= i2c_get_clientdata(client
);
225 mutex_destroy(&ledbar
->lock
);
230 static const struct i2c_device_id ubnt_ledbar_id
[] = {
231 { "ubnt-ledbar", 0 },
234 MODULE_DEVICE_TABLE(i2c
, ubnt_ledbar_id
);
236 static const struct of_device_id of_ubnt_ledbar_match
[] = {
237 { .compatible
= "ubnt,ledbar", },
240 MODULE_DEVICE_TABLE(of
, of_ubnt_ledbar_match
);
242 static struct i2c_driver ubnt_ledbar_driver
= {
244 .name
= "ubnt-ledbar",
245 .of_match_table
= of_ubnt_ledbar_match
,
247 .probe
= ubnt_ledbar_probe
,
248 .remove
= ubnt_ledbar_remove
,
249 .id_table
= ubnt_ledbar_id
,
251 module_i2c_driver(ubnt_ledbar_driver
);
253 MODULE_DESCRIPTION("Ubiquiti LEDBAR driver");
254 MODULE_AUTHOR("David Bauer <mail@david-bauer.net>");
255 MODULE_LICENSE("GPL v2");