]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/leds/led-core.c
Merge tag 'for-5.4/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/devic...
[linux.git] / drivers / leds / led-core.c
index 7107cd7e87cfde458fb0f24d0183c6de1566fa82..f1f718dbe0f873ebf72ac0ff10a0e1db8ab01496 100644 (file)
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
+#include <linux/property.h>
 #include <linux/rwsem.h>
 #include <linux/slab.h>
+#include <uapi/linux/uleds.h>
 #include "leds.h"
 
 DECLARE_RWSEM(leds_list_lock);
@@ -23,6 +25,18 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
 LIST_HEAD(leds_list);
 EXPORT_SYMBOL_GPL(leds_list);
 
+const char * const led_colors[LED_COLOR_ID_MAX] = {
+       [LED_COLOR_ID_WHITE] = "white",
+       [LED_COLOR_ID_RED] = "red",
+       [LED_COLOR_ID_GREEN] = "green",
+       [LED_COLOR_ID_BLUE] = "blue",
+       [LED_COLOR_ID_AMBER] = "amber",
+       [LED_COLOR_ID_VIOLET] = "violet",
+       [LED_COLOR_ID_YELLOW] = "yellow",
+       [LED_COLOR_ID_IR] = "ir",
+};
+EXPORT_SYMBOL_GPL(led_colors);
+
 static int __led_set_brightness(struct led_classdev *led_cdev,
                                enum led_brightness value)
 {
@@ -310,14 +324,11 @@ EXPORT_SYMBOL_GPL(led_update_brightness);
 
 u32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
 {
-       struct device_node *np = dev_of_node(led_cdev->dev);
+       struct fwnode_handle *fwnode = led_cdev->dev->fwnode;
        u32 *pattern;
        int count;
 
-       if (!np)
-               return NULL;
-
-       count = of_property_count_u32_elems(np, "led-pattern");
+       count = fwnode_property_count_u32(fwnode, "led-pattern");
        if (count < 0)
                return NULL;
 
@@ -325,7 +336,7 @@ u32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
        if (!pattern)
                return NULL;
 
-       if (of_property_read_u32_array(np, "led-pattern", pattern, count)) {
+       if (fwnode_property_read_u32_array(fwnode, "led-pattern", pattern, count)) {
                kfree(pattern);
                return NULL;
        }
@@ -353,3 +364,116 @@ void led_sysfs_enable(struct led_classdev *led_cdev)
        led_cdev->flags &= ~LED_SYSFS_DISABLE;
 }
 EXPORT_SYMBOL_GPL(led_sysfs_enable);
+
+static void led_parse_fwnode_props(struct device *dev,
+                                  struct fwnode_handle *fwnode,
+                                  struct led_properties *props)
+{
+       int ret;
+
+       if (!fwnode)
+               return;
+
+       if (fwnode_property_present(fwnode, "label")) {
+               ret = fwnode_property_read_string(fwnode, "label", &props->label);
+               if (ret)
+                       dev_err(dev, "Error parsing 'label' property (%d)\n", ret);
+               return;
+       }
+
+       if (fwnode_property_present(fwnode, "color")) {
+               ret = fwnode_property_read_u32(fwnode, "color", &props->color);
+               if (ret)
+                       dev_err(dev, "Error parsing 'color' property (%d)\n", ret);
+               else if (props->color >= LED_COLOR_ID_MAX)
+                       dev_err(dev, "LED color identifier out of range\n");
+               else
+                       props->color_present = true;
+       }
+
+
+       if (!fwnode_property_present(fwnode, "function"))
+               return;
+
+       ret = fwnode_property_read_string(fwnode, "function", &props->function);
+       if (ret) {
+               dev_err(dev,
+                       "Error parsing 'function' property (%d)\n",
+                       ret);
+       }
+
+       if (!fwnode_property_present(fwnode, "function-enumerator"))
+               return;
+
+       ret = fwnode_property_read_u32(fwnode, "function-enumerator",
+                                      &props->func_enum);
+       if (ret) {
+               dev_err(dev,
+                       "Error parsing 'function-enumerator' property (%d)\n",
+                       ret);
+       } else {
+               props->func_enum_present = true;
+       }
+}
+
+int led_compose_name(struct device *dev, struct led_init_data *init_data,
+                    char *led_classdev_name)
+{
+       struct led_properties props = {};
+       struct fwnode_handle *fwnode = init_data->fwnode;
+       const char *devicename = init_data->devicename;
+
+       if (!led_classdev_name)
+               return -EINVAL;
+
+       led_parse_fwnode_props(dev, fwnode, &props);
+
+       if (props.label) {
+               /*
+                * If init_data.devicename is NULL, then it indicates that
+                * DT label should be used as-is for LED class device name.
+                * Otherwise the label is prepended with devicename to compose
+                * the final LED class device name.
+                */
+               if (!devicename) {
+                       strscpy(led_classdev_name, props.label,
+                               LED_MAX_NAME_SIZE);
+               } else {
+                       snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
+                                devicename, props.label);
+               }
+       } else if (props.function || props.color_present) {
+               char tmp_buf[LED_MAX_NAME_SIZE];
+
+               if (props.func_enum_present) {
+                       snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
+                                props.color_present ? led_colors[props.color] : "",
+                                props.function ?: "", props.func_enum);
+               } else {
+                       snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
+                                props.color_present ? led_colors[props.color] : "",
+                                props.function ?: "");
+               }
+               if (init_data->devname_mandatory) {
+                       snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
+                                devicename, tmp_buf);
+               } else {
+                       strscpy(led_classdev_name, tmp_buf, LED_MAX_NAME_SIZE);
+
+               }
+       } else if (init_data->default_label) {
+               if (!devicename) {
+                       dev_err(dev, "Legacy LED naming requires devicename segment");
+                       return -EINVAL;
+               }
+               snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
+                        devicename, init_data->default_label);
+       } else if (is_of_node(fwnode)) {
+               strscpy(led_classdev_name, to_of_node(fwnode)->name,
+                       LED_MAX_NAME_SIZE);
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(led_compose_name);