.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * LED Class Core |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright 2005-2006 Openedhand Ltd. |
---|
5 | 6 | * |
---|
6 | 7 | * 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 | | - * |
---|
12 | 8 | */ |
---|
13 | 9 | |
---|
14 | 10 | #include <linux/kernel.h> |
---|
.. | .. |
---|
16 | 12 | #include <linux/list.h> |
---|
17 | 13 | #include <linux/module.h> |
---|
18 | 14 | #include <linux/mutex.h> |
---|
| 15 | +#include <linux/of.h> |
---|
| 16 | +#include <linux/property.h> |
---|
19 | 17 | #include <linux/rwsem.h> |
---|
| 18 | +#include <linux/slab.h> |
---|
| 19 | +#include <uapi/linux/uleds.h> |
---|
20 | 20 | #include "leds.h" |
---|
21 | 21 | |
---|
22 | 22 | DECLARE_RWSEM(leds_list_lock); |
---|
.. | .. |
---|
24 | 24 | |
---|
25 | 25 | LIST_HEAD(leds_list); |
---|
26 | 26 | 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); |
---|
27 | 41 | |
---|
28 | 42 | static int __led_set_brightness(struct led_classdev *led_cdev, |
---|
29 | 43 | enum led_brightness value) |
---|
.. | .. |
---|
310 | 324 | } |
---|
311 | 325 | EXPORT_SYMBOL_GPL(led_update_brightness); |
---|
312 | 326 | |
---|
| 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 | + |
---|
313 | 352 | /* Caller must ensure led_cdev->led_access held */ |
---|
314 | 353 | void led_sysfs_disable(struct led_classdev *led_cdev) |
---|
315 | 354 | { |
---|
.. | .. |
---|
327 | 366 | led_cdev->flags &= ~LED_SYSFS_DISABLE; |
---|
328 | 367 | } |
---|
329 | 368 | 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); |
---|