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 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
37 struct i2c_client
*client
;
38 struct led_classdev led_red
;
39 struct led_classdev led_green
;
40 struct led_classdev led_blue
;
41 struct gpio_desc
*enable_gpio
;
44 static int ubnt_ledbar_perform_transaction(struct ubnt_ledbar
*ledbar
,
50 for (i
= 0; i
< UBNT_LEDBAR_TRANSACTION_LENGTH
; i
++)
51 i2c_smbus_write_byte(ledbar
->client
, transaction
[i
]);
53 return i2c_smbus_read_byte(ledbar
->client
);
56 static int ubnt_ledbar_apply_state(struct ubnt_ledbar
*ledbar
)
58 char setup_msg
[UBNT_LEDBAR_TRANSACTION_LENGTH
] = {0x40, 0x10, 0x00, 0x00,
59 0x00, 0x00, 0x00, 0x11};
60 char led_msg
[UBNT_LEDBAR_TRANSACTION_LENGTH
] = {0x40, 0x00, 0x00, 0x00,
61 0x00, 0x00, 0x01, 0x00};
65 mutex_lock(&ledbar
->lock
);
67 led_msg
[UBNT_LEDBAR_TRANSACTION_BLUE_IDX
] = ledbar
->led_blue
.brightness
;
68 led_msg
[UBNT_LEDBAR_TRANSACTION_GREEN_IDX
] = ledbar
->led_green
.brightness
;
69 led_msg
[UBNT_LEDBAR_TRANSACTION_RED_IDX
] = ledbar
->led_red
.brightness
;
71 gpiod_set_raw_value(ledbar
->enable_gpio
, 1);
75 i2c_response
= ubnt_ledbar_perform_transaction(ledbar
, setup_msg
);
76 if (i2c_response
!= UBNT_LEDBAR_TRANSACTION_SUCCESS
) {
77 dev_err(&ledbar
->client
->dev
, "Error initializing LED transaction: %02x\n", ret
);
82 i2c_response
= ubnt_ledbar_perform_transaction(ledbar
, led_msg
);
83 if (i2c_response
!= UBNT_LEDBAR_TRANSACTION_SUCCESS
) {
84 dev_err(&ledbar
->client
->dev
, "Failed LED transaction: %02x\n", ret
);
91 gpiod_set_raw_value(ledbar
->enable_gpio
, 0);
93 mutex_unlock(&ledbar
->lock
);
98 #define UBNT_LEDBAR_CONTROL_RGBS(name) \
99 static int ubnt_ledbar_set_##name##_brightness(struct led_classdev *led_cdev,\
100 enum led_brightness value) \
102 struct ubnt_ledbar *ledbar = \
103 container_of(led_cdev, struct ubnt_ledbar, led_##name); \
105 led_cdev->brightness = value; \
106 ret = ubnt_ledbar_apply_state(ledbar); \
110 UBNT_LEDBAR_CONTROL_RGBS(red
);
111 UBNT_LEDBAR_CONTROL_RGBS(green
);
112 UBNT_LEDBAR_CONTROL_RGBS(blue
);
115 static int ubnt_ledbar_init_led(struct device_node
*np
, struct ubnt_ledbar
*ledbar
,
116 struct led_classdev
*led_cdev
)
118 struct led_init_data init_data
= {};
124 init_data
.fwnode
= of_fwnode_handle(np
);
126 led_cdev
->max_brightness
= UBNT_LEDBAR_MAX_BRIGHTNESS
;
128 ret
= devm_led_classdev_register_ext(&ledbar
->client
->dev
, led_cdev
,
131 dev_err(&ledbar
->client
->dev
, "led register err: %d\n", ret
);
137 static int ubnt_ledbar_probe(struct i2c_client
*client
,
138 const struct i2c_device_id
*id
)
140 struct device_node
*np
= client
->dev
.of_node
;
141 struct ubnt_ledbar
*ledbar
;
144 ledbar
= devm_kzalloc(&client
->dev
, sizeof(*ledbar
), GFP_KERNEL
);
148 ledbar
->enable_gpio
= devm_gpiod_get(&client
->dev
, "enable", GPIOD_OUT_LOW
);
150 if (IS_ERR(ledbar
->enable_gpio
)) {
151 ret
= PTR_ERR(ledbar
->enable_gpio
);
152 dev_err(&client
->dev
, "Failed to get enable gpio: %d\n", ret
);
156 gpiod_direction_output(ledbar
->enable_gpio
, 0);
158 ledbar
->client
= client
;
160 mutex_init(&ledbar
->lock
);
162 i2c_set_clientdata(client
, ledbar
);
164 ledbar
->led_red
.brightness_set_blocking
= ubnt_ledbar_set_red_brightness
;
165 ubnt_ledbar_init_led(of_get_child_by_name(np
, "red"), ledbar
, &ledbar
->led_red
);
167 ledbar
->led_green
.brightness_set_blocking
= ubnt_ledbar_set_green_brightness
;
168 ubnt_ledbar_init_led(of_get_child_by_name(np
, "green"), ledbar
, &ledbar
->led_green
);
170 ledbar
->led_blue
.brightness_set_blocking
= ubnt_ledbar_set_blue_brightness
;
171 ubnt_ledbar_init_led(of_get_child_by_name(np
, "blue"), ledbar
, &ledbar
->led_blue
);
173 return ubnt_ledbar_apply_state(ledbar
);
176 static int ubnt_ledbar_remove(struct i2c_client
*client
)
178 struct ubnt_ledbar
*ledbar
= i2c_get_clientdata(client
);
180 mutex_destroy(&ledbar
->lock
);
185 static const struct i2c_device_id ubnt_ledbar_id
[] = {
186 { "ubnt-ledbar", 0 },
189 MODULE_DEVICE_TABLE(i2c
, ubnt_ledbar_id
);
191 static const struct of_device_id of_ubnt_ledbar_match
[] = {
192 { .compatible
= "ubnt,ledbar", },
195 MODULE_DEVICE_TABLE(of
, of_ubnt_ledbar_match
);
197 static struct i2c_driver ubnt_ledbar_driver
= {
199 .name
= "ubnt-ledbar",
200 .of_match_table
= of_ubnt_ledbar_match
,
202 .probe
= ubnt_ledbar_probe
,
203 .remove
= ubnt_ledbar_remove
,
204 .id_table
= ubnt_ledbar_id
,
206 module_i2c_driver(ubnt_ledbar_driver
);
208 MODULE_DESCRIPTION("Ubiquiti LEDBAR driver");
209 MODULE_AUTHOR("David Bauer <mail@david-bauer.net>");
210 MODULE_LICENSE("GPL v2");