Skip to content

Commit c223d9c

Browse files
geertuojeda
authored andcommitted
auxdisplay: ht16k33: Add LED support
Instantiate a single LED based on the "led" subnode in DT. This allows the user to control display brightness and blinking (backed by hardware support) through the LED class API and triggers, and exposes the display color. The LED will be named "auxdisplay:<color>:<function>". When running in dot-matrix mode and if no "led" subnode is found, the driver falls back to the traditional backlight mode, to preserve backwards compatibility. Signed-off-by: Geert Uytterhoeven <[email protected]> Reviewed-by: Marek Behún <[email protected]> Reviewed-by: Robin van der Gracht <[email protected]> Signed-off-by: Miguel Ojeda <[email protected]>
1 parent 2904c01 commit c223d9c

File tree

2 files changed

+112
-16
lines changed

2 files changed

+112
-16
lines changed

drivers/auxdisplay/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ config HT16K33
176176
select FB_SYS_IMAGEBLIT
177177
select INPUT_MATRIXKMAP
178178
select FB_BACKLIGHT
179+
select NEW_LEDS
180+
select LEDS_CLASS
179181
select LINEDISP
180182
help
181183
Say yes here to add support for Holtek HT16K33, RAM mapping 16*8

drivers/auxdisplay/ht16k33.c

Lines changed: 110 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/backlight.h>
1919
#include <linux/input.h>
2020
#include <linux/input/matrix_keypad.h>
21+
#include <linux/leds.h>
2122
#include <linux/workqueue.h>
2223
#include <linux/mm.h>
2324

@@ -34,6 +35,10 @@
3435

3536
#define REG_DISPLAY_SETUP 0x80
3637
#define REG_DISPLAY_SETUP_ON BIT(0)
38+
#define REG_DISPLAY_SETUP_BLINK_OFF (0 << 1)
39+
#define REG_DISPLAY_SETUP_BLINK_2HZ (1 << 1)
40+
#define REG_DISPLAY_SETUP_BLINK_1HZ (2 << 1)
41+
#define REG_DISPLAY_SETUP_BLINK_0HZ5 (3 << 1)
3742

3843
#define REG_ROWINT_SET 0xA0
3944
#define REG_ROWINT_SET_INT_EN BIT(0)
@@ -94,12 +99,14 @@ struct ht16k33_seg {
9499
struct ht16k33_priv {
95100
struct i2c_client *client;
96101
struct delayed_work work;
102+
struct led_classdev led;
97103
struct ht16k33_keypad keypad;
98104
union {
99105
struct ht16k33_fbdev fbdev;
100106
struct ht16k33_seg seg;
101107
};
102108
enum display_type type;
109+
uint8_t blink;
103110
};
104111

105112
static const struct fb_fix_screeninfo ht16k33_fb_fix = {
@@ -158,7 +165,7 @@ static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store);
158165

159166
static int ht16k33_display_on(struct ht16k33_priv *priv)
160167
{
161-
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON;
168+
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON | priv->blink;
162169

163170
return i2c_smbus_write_byte(priv->client, data);
164171
}
@@ -173,8 +180,10 @@ static int ht16k33_brightness_set(struct ht16k33_priv *priv,
173180
{
174181
int err;
175182

176-
if (brightness == 0)
183+
if (brightness == 0) {
184+
priv->blink = REG_DISPLAY_SETUP_BLINK_OFF;
177185
return ht16k33_display_off(priv);
186+
}
178187

179188
err = ht16k33_display_on(priv);
180189
if (err)
@@ -184,6 +193,49 @@ static int ht16k33_brightness_set(struct ht16k33_priv *priv,
184193
REG_BRIGHTNESS | (brightness - 1));
185194
}
186195

196+
static int ht16k33_brightness_set_blocking(struct led_classdev *led_cdev,
197+
enum led_brightness brightness)
198+
{
199+
struct ht16k33_priv *priv = container_of(led_cdev, struct ht16k33_priv,
200+
led);
201+
202+
return ht16k33_brightness_set(priv, brightness);
203+
}
204+
205+
static int ht16k33_blink_set(struct led_classdev *led_cdev,
206+
unsigned long *delay_on, unsigned long *delay_off)
207+
{
208+
struct ht16k33_priv *priv = container_of(led_cdev, struct ht16k33_priv,
209+
led);
210+
unsigned int delay;
211+
uint8_t blink;
212+
int err;
213+
214+
if (!*delay_on && !*delay_off) {
215+
blink = REG_DISPLAY_SETUP_BLINK_1HZ;
216+
delay = 1000;
217+
} else if (*delay_on <= 750) {
218+
blink = REG_DISPLAY_SETUP_BLINK_2HZ;
219+
delay = 500;
220+
} else if (*delay_on <= 1500) {
221+
blink = REG_DISPLAY_SETUP_BLINK_1HZ;
222+
delay = 1000;
223+
} else {
224+
blink = REG_DISPLAY_SETUP_BLINK_0HZ5;
225+
delay = 2000;
226+
}
227+
228+
err = i2c_smbus_write_byte(priv->client,
229+
REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON |
230+
blink);
231+
if (err)
232+
return err;
233+
234+
priv->blink = blink;
235+
*delay_on = *delay_off = delay;
236+
return 0;
237+
}
238+
187239
static void ht16k33_fb_queue(struct ht16k33_priv *priv)
188240
{
189241
struct ht16k33_fbdev *fbdev = &priv->fbdev;
@@ -435,6 +487,35 @@ static void ht16k33_seg14_update(struct work_struct *work)
435487
i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);
436488
}
437489

490+
static int ht16k33_led_probe(struct device *dev, struct led_classdev *led,
491+
unsigned int brightness)
492+
{
493+
struct led_init_data init_data = {};
494+
struct device_node *node;
495+
int err;
496+
497+
/* The LED is optional */
498+
node = of_get_child_by_name(dev->of_node, "led");
499+
if (!node)
500+
return 0;
501+
502+
init_data.fwnode = of_fwnode_handle(node);
503+
init_data.devicename = "auxdisplay";
504+
init_data.devname_mandatory = true;
505+
506+
led->brightness_set_blocking = ht16k33_brightness_set_blocking;
507+
led->blink_set = ht16k33_blink_set;
508+
led->flags = LED_CORE_SUSPENDRESUME;
509+
led->brightness = brightness;
510+
led->max_brightness = MAX_BRIGHTNESS;
511+
512+
err = devm_led_classdev_register_ext(dev, led, &init_data);
513+
if (err)
514+
dev_err(dev, "Failed to register LED\n");
515+
516+
return err;
517+
}
518+
438519
static int ht16k33_keypad_probe(struct i2c_client *client,
439520
struct ht16k33_keypad *keypad)
440521
{
@@ -508,25 +589,33 @@ static int ht16k33_fbdev_probe(struct device *dev, struct ht16k33_priv *priv,
508589
uint32_t brightness)
509590
{
510591
struct ht16k33_fbdev *fbdev = &priv->fbdev;
511-
struct backlight_properties bl_props;
512-
struct backlight_device *bl;
592+
struct backlight_device *bl = NULL;
513593
int err;
514594

515-
/* Backlight */
516-
memset(&bl_props, 0, sizeof(struct backlight_properties));
517-
bl_props.type = BACKLIGHT_RAW;
518-
bl_props.max_brightness = MAX_BRIGHTNESS;
595+
if (priv->led.dev) {
596+
err = ht16k33_brightness_set(priv, brightness);
597+
if (err)
598+
return err;
599+
} else {
600+
/* backwards compatibility with DT lacking an led subnode */
601+
struct backlight_properties bl_props;
602+
603+
memset(&bl_props, 0, sizeof(struct backlight_properties));
604+
bl_props.type = BACKLIGHT_RAW;
605+
bl_props.max_brightness = MAX_BRIGHTNESS;
606+
607+
bl = devm_backlight_device_register(dev, DRIVER_NAME"-bl", dev,
608+
priv, &ht16k33_bl_ops,
609+
&bl_props);
610+
if (IS_ERR(bl)) {
611+
dev_err(dev, "failed to register backlight\n");
612+
return PTR_ERR(bl);
613+
}
519614

520-
bl = devm_backlight_device_register(dev, DRIVER_NAME"-bl", dev, priv,
521-
&ht16k33_bl_ops, &bl_props);
522-
if (IS_ERR(bl)) {
523-
dev_err(dev, "failed to register backlight\n");
524-
return PTR_ERR(bl);
615+
bl->props.brightness = brightness;
616+
ht16k33_bl_update_status(bl);
525617
}
526618

527-
bl->props.brightness = brightness;
528-
ht16k33_bl_update_status(bl);
529-
530619
/* Framebuffer (2 bytes per column) */
531620
BUILD_BUG_ON(PAGE_SIZE < HT16K33_FB_SIZE);
532621
fbdev->buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);
@@ -663,6 +752,11 @@ static int ht16k33_probe(struct i2c_client *client)
663752
dft_brightness = MAX_BRIGHTNESS;
664753
}
665754

755+
/* LED */
756+
err = ht16k33_led_probe(dev, &priv->led, dft_brightness);
757+
if (err)
758+
return err;
759+
666760
/* Keypad */
667761
if (client->irq > 0) {
668762
err = ht16k33_keypad_probe(client, &priv->keypad);

0 commit comments

Comments
 (0)