hc
2024-05-10 61598093bbdd283a7edc367d900f223070ead8d2
kernel/drivers/leds/led-core.c
....@@ -1,14 +1,10 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * LED Class Core
34 *
45 * Copyright 2005-2006 Openedhand Ltd.
56 *
67 * Author: Richard Purdie <rpurdie@openedhand.com>
7
- *
8
- * This program is free software; you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License version 2 as
10
- * published by the Free Software Foundation.
11
- *
128 */
139
1410 #include <linux/kernel.h>
....@@ -16,7 +12,11 @@
1612 #include <linux/list.h>
1713 #include <linux/module.h>
1814 #include <linux/mutex.h>
15
+#include <linux/of.h>
16
+#include <linux/property.h>
1917 #include <linux/rwsem.h>
18
+#include <linux/slab.h>
19
+#include <uapi/linux/uleds.h>
2020 #include "leds.h"
2121
2222 DECLARE_RWSEM(leds_list_lock);
....@@ -24,6 +24,20 @@
2424
2525 LIST_HEAD(leds_list);
2626 EXPORT_SYMBOL_GPL(leds_list);
27
+
28
+const char * const led_colors[LED_COLOR_ID_MAX] = {
29
+ [LED_COLOR_ID_WHITE] = "white",
30
+ [LED_COLOR_ID_RED] = "red",
31
+ [LED_COLOR_ID_GREEN] = "green",
32
+ [LED_COLOR_ID_BLUE] = "blue",
33
+ [LED_COLOR_ID_AMBER] = "amber",
34
+ [LED_COLOR_ID_VIOLET] = "violet",
35
+ [LED_COLOR_ID_YELLOW] = "yellow",
36
+ [LED_COLOR_ID_IR] = "ir",
37
+ [LED_COLOR_ID_MULTI] = "multicolor",
38
+ [LED_COLOR_ID_RGB] = "rgb",
39
+};
40
+EXPORT_SYMBOL_GPL(led_colors);
2741
2842 static int __led_set_brightness(struct led_classdev *led_cdev,
2943 enum led_brightness value)
....@@ -310,6 +324,31 @@
310324 }
311325 EXPORT_SYMBOL_GPL(led_update_brightness);
312326
327
+u32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
328
+{
329
+ struct fwnode_handle *fwnode = led_cdev->dev->fwnode;
330
+ u32 *pattern;
331
+ int count;
332
+
333
+ count = fwnode_property_count_u32(fwnode, "led-pattern");
334
+ if (count < 0)
335
+ return NULL;
336
+
337
+ pattern = kcalloc(count, sizeof(*pattern), GFP_KERNEL);
338
+ if (!pattern)
339
+ return NULL;
340
+
341
+ if (fwnode_property_read_u32_array(fwnode, "led-pattern", pattern, count)) {
342
+ kfree(pattern);
343
+ return NULL;
344
+ }
345
+
346
+ *size = count;
347
+
348
+ return pattern;
349
+}
350
+EXPORT_SYMBOL_GPL(led_get_default_pattern);
351
+
313352 /* Caller must ensure led_cdev->led_access held */
314353 void led_sysfs_disable(struct led_classdev *led_cdev)
315354 {
....@@ -327,3 +366,120 @@
327366 led_cdev->flags &= ~LED_SYSFS_DISABLE;
328367 }
329368 EXPORT_SYMBOL_GPL(led_sysfs_enable);
369
+
370
+static void led_parse_fwnode_props(struct device *dev,
371
+ struct fwnode_handle *fwnode,
372
+ struct led_properties *props)
373
+{
374
+ int ret;
375
+
376
+ if (!fwnode)
377
+ return;
378
+
379
+ if (fwnode_property_present(fwnode, "label")) {
380
+ ret = fwnode_property_read_string(fwnode, "label", &props->label);
381
+ if (ret)
382
+ dev_err(dev, "Error parsing 'label' property (%d)\n", ret);
383
+ return;
384
+ }
385
+
386
+ if (fwnode_property_present(fwnode, "color")) {
387
+ ret = fwnode_property_read_u32(fwnode, "color", &props->color);
388
+ if (ret)
389
+ dev_err(dev, "Error parsing 'color' property (%d)\n", ret);
390
+ else if (props->color >= LED_COLOR_ID_MAX)
391
+ dev_err(dev, "LED color identifier out of range\n");
392
+ else
393
+ props->color_present = true;
394
+ }
395
+
396
+
397
+ if (!fwnode_property_present(fwnode, "function"))
398
+ return;
399
+
400
+ ret = fwnode_property_read_string(fwnode, "function", &props->function);
401
+ if (ret) {
402
+ dev_err(dev,
403
+ "Error parsing 'function' property (%d)\n",
404
+ ret);
405
+ }
406
+
407
+ if (!fwnode_property_present(fwnode, "function-enumerator"))
408
+ return;
409
+
410
+ ret = fwnode_property_read_u32(fwnode, "function-enumerator",
411
+ &props->func_enum);
412
+ if (ret) {
413
+ dev_err(dev,
414
+ "Error parsing 'function-enumerator' property (%d)\n",
415
+ ret);
416
+ } else {
417
+ props->func_enum_present = true;
418
+ }
419
+}
420
+
421
+int led_compose_name(struct device *dev, struct led_init_data *init_data,
422
+ char *led_classdev_name)
423
+{
424
+ struct led_properties props = {};
425
+ struct fwnode_handle *fwnode = init_data->fwnode;
426
+ const char *devicename = init_data->devicename;
427
+
428
+ /* We want to label LEDs that can produce full range of colors
429
+ * as RGB, not multicolor */
430
+ BUG_ON(props.color == LED_COLOR_ID_MULTI);
431
+
432
+ if (!led_classdev_name)
433
+ return -EINVAL;
434
+
435
+ led_parse_fwnode_props(dev, fwnode, &props);
436
+
437
+ if (props.label) {
438
+ /*
439
+ * If init_data.devicename is NULL, then it indicates that
440
+ * DT label should be used as-is for LED class device name.
441
+ * Otherwise the label is prepended with devicename to compose
442
+ * the final LED class device name.
443
+ */
444
+ if (!devicename) {
445
+ strscpy(led_classdev_name, props.label,
446
+ LED_MAX_NAME_SIZE);
447
+ } else {
448
+ snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
449
+ devicename, props.label);
450
+ }
451
+ } else if (props.function || props.color_present) {
452
+ char tmp_buf[LED_MAX_NAME_SIZE];
453
+
454
+ if (props.func_enum_present) {
455
+ snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
456
+ props.color_present ? led_colors[props.color] : "",
457
+ props.function ?: "", props.func_enum);
458
+ } else {
459
+ snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
460
+ props.color_present ? led_colors[props.color] : "",
461
+ props.function ?: "");
462
+ }
463
+ if (init_data->devname_mandatory) {
464
+ snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
465
+ devicename, tmp_buf);
466
+ } else {
467
+ strscpy(led_classdev_name, tmp_buf, LED_MAX_NAME_SIZE);
468
+
469
+ }
470
+ } else if (init_data->default_label) {
471
+ if (!devicename) {
472
+ dev_err(dev, "Legacy LED naming requires devicename segment");
473
+ return -EINVAL;
474
+ }
475
+ snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
476
+ devicename, init_data->default_label);
477
+ } else if (is_of_node(fwnode)) {
478
+ strscpy(led_classdev_name, to_of_node(fwnode)->name,
479
+ LED_MAX_NAME_SIZE);
480
+ } else
481
+ return -EINVAL;
482
+
483
+ return 0;
484
+}
485
+EXPORT_SYMBOL_GPL(led_compose_name);