drivers: leds: leds-multi: control several LEDs synchronously

A multi LED may control up to 9 single LED by different
brightness values.
Each decimal place represents one single LED:

  0 = off
  1-8 =  max_brightness * n / 8
  9 = max_brightness

The timer trigger may toggle the LED-array while
the brightness value of the multi-led defines,
which LEDs of the array toggle with which brightness.

The "pattern" trigger may be used to switch the colors
for a specific time to blink any pattern in multiple colors.

This allows an easy implementation of multicolor LEDs,
without API extension or breaks.

Signed-off-by: Christian Hohnstaedt <Christian.Hohnstaedt@wago.com>
This commit is contained in:
Christian Hohnstaedt
2020-03-24 12:25:36 +01:00
committed by Oleg Karfich
parent 2c3221426a
commit 3254bb1f3c
3 changed files with 151 additions and 0 deletions
+14
View File
@@ -892,6 +892,20 @@ config LEDS_ACER_A500
This option enables support for the Power Button LED of
Acer Iconia Tab A500.
config LEDS_MULTI
tristate "Control multiple LEDs by one trigger"
depends on LEDS_CLASS
help
This is a virtual LED, controlling up to 9 simple LEDs.
Each LED is controlled by a decimal digit of the brightness
of this virtual LED. It may for example control the 3 single LEDs of
an RGB multi-color LED. The timer trigger turns them on and off
while the brightness value controls the color.
Together with the pattern trigger, even "enterprisey"
error codes blinked to users, running lights and other things
are possible without complex user-space code.
If unsure say N.
source "drivers/leds/blink/Kconfig"
comment "Flash and Torch LED drivers"
+1
View File
@@ -88,6 +88,7 @@ obj-$(CONFIG_LEDS_TURRIS_OMNIA) += leds-turris-omnia.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
obj-$(CONFIG_LEDS_MULTI) += leds-multi.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o
+136
View File
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for orchestrating several LEDs
*
* Copyright (C) 2020 Christian Hohnstaedt <christian.hohnstaedt@wago.com>
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/of.h>
#include "leds.h"
#define DRVNAME "multi-led"
#define MAX_LEDS 9
struct multi_led {
struct led_classdev cdev;
int num_leds;
struct led_classdev *leds[MAX_LEDS];
};
static void multi_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct multi_led *mled = container_of(led_cdev, struct multi_led, cdev);
int i, v;
for (i = 0, v = value; i < mled->num_leds; i++, v /= 10) {
int val = mled->leds[i]->max_brightness;
switch (v % 10) {
case 0:
val = 0;
break;
case 9:
break;
default:
if (val >= 8)
val = val * (v % 10) / 8;
break;
}
led_set_brightness(mled->leds[i], val);
}
}
static struct led_classdev *find_child_led(phandle phandle)
{
struct led_classdev *led;
struct device_node *node = of_find_node_by_phandle(phandle);
const char *label;
int len;
len = strlen(label);
down_read(&leds_list_lock);
list_for_each_entry(led, &leds_list, node) {
if (node == led->dev->of_node) {
up_read(&leds_list_lock);
return led;
}
}
up_read(&leds_list_lock);
return NULL;
}
static int multi_led_probe(struct platform_device *pdev)
{
int size, i;
struct multi_led *mled;
const __be32 *list;
struct device_node *np = pdev->dev.of_node;
struct property *prop;
mled = devm_kzalloc(&pdev->dev, sizeof(*mled), GFP_KERNEL);
if (!mled)
return -ENOMEM;
prop = of_find_property(np, "leds", &size);
mled->num_leds = size / sizeof(*list);
if (mled->num_leds > MAX_LEDS)
return -EINVAL;
list = prop->value;
mled->cdev.max_brightness = 1;
mled->cdev.brightness_set = multi_led_set;
for (i = 0; i < mled->num_leds; i++) {
mled->leds[i] = find_child_led(be32_to_cpup(list++));
mled->cdev.max_brightness *= 10;
if (mled->leds[i] == NULL)
return -EPROBE_DEFER;
led_trigger_remove(mled->leds[i]);
}
mled->cdev.max_brightness--;
if (of_property_read_string(np, "label", &mled->cdev.name))
return -EINVAL;
return devm_led_classdev_register(&pdev->dev, &mled->cdev);
}
static const struct of_device_id multi_led_of_match[] = {
{ .compatible = "multi_led", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, multi_led_of_match);
static struct platform_driver multi_led_driver = {
.probe = multi_led_probe,
.driver = {
.name = DRVNAME,
.of_match_table = of_match_ptr(multi_led_of_match),
},
};
static int __init multi_led_init(void)
{
return platform_driver_register(&multi_led_driver);
}
static void __exit multi_led_exit(void)
{
platform_driver_unregister(&multi_led_driver);
}
module_init(multi_led_init);
module_exit(multi_led_exit);
MODULE_AUTHOR("Christian Hohnstaedt <christian.hohnstaedt@wago.com>");
MODULE_DESCRIPTION("Multi LED driver");
MODULE_LICENSE("GPL");