| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- // SPDX-License-Identifier: GPL-2.0
- // LED Multicolor class interface
- // Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/
- // Author: Dan Murphy <dmurphy@ti.com>
- #include <linux/device.h>
- #include <linux/init.h>
- #include <linux/led-class-multicolor.h>
- #include <linux/math.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- #include "leds.h"
- int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
- enum led_brightness brightness)
- {
- struct led_classdev *led_cdev = &mcled_cdev->led_cdev;
- int i;
- for (i = 0; i < mcled_cdev->num_colors; i++)
- mcled_cdev->subled_info[i].brightness =
- DIV_ROUND_CLOSEST(brightness *
- mcled_cdev->subled_info[i].intensity,
- led_cdev->max_brightness);
- return 0;
- }
- EXPORT_SYMBOL_GPL(led_mc_calc_color_components);
- static ssize_t multi_intensity_store(struct device *dev,
- struct device_attribute *intensity_attr,
- const char *buf, size_t size)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
- int nrchars, offset = 0;
- int intensity_value[LED_COLOR_ID_MAX];
- int i;
- ssize_t ret;
- mutex_lock(&led_cdev->led_access);
- for (i = 0; i < mcled_cdev->num_colors; i++) {
- ret = sscanf(buf + offset, "%i%n",
- &intensity_value[i], &nrchars);
- if (ret != 1) {
- ret = -EINVAL;
- goto err_out;
- }
- offset += nrchars;
- }
- offset++;
- if (offset < size) {
- ret = -EINVAL;
- goto err_out;
- }
- for (i = 0; i < mcled_cdev->num_colors; i++)
- mcled_cdev->subled_info[i].intensity = intensity_value[i];
- led_set_brightness(led_cdev, led_cdev->brightness);
- ret = size;
- err_out:
- mutex_unlock(&led_cdev->led_access);
- return ret;
- }
- static ssize_t multi_intensity_show(struct device *dev,
- struct device_attribute *intensity_attr,
- char *buf)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
- int len = 0;
- int i;
- for (i = 0; i < mcled_cdev->num_colors; i++) {
- len += sprintf(buf + len, "%d",
- mcled_cdev->subled_info[i].intensity);
- if (i < mcled_cdev->num_colors - 1)
- len += sprintf(buf + len, " ");
- }
- buf[len++] = '\n';
- return len;
- }
- static DEVICE_ATTR_RW(multi_intensity);
- static ssize_t multi_index_show(struct device *dev,
- struct device_attribute *multi_index_attr,
- char *buf)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
- int len = 0;
- int index;
- int i;
- for (i = 0; i < mcled_cdev->num_colors; i++) {
- index = mcled_cdev->subled_info[i].color_index;
- len += sprintf(buf + len, "%s", led_get_color_name(index));
- if (i < mcled_cdev->num_colors - 1)
- len += sprintf(buf + len, " ");
- }
- buf[len++] = '\n';
- return len;
- }
- static DEVICE_ATTR_RO(multi_index);
- static struct attribute *led_multicolor_attrs[] = {
- &dev_attr_multi_intensity.attr,
- &dev_attr_multi_index.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(led_multicolor);
- int led_classdev_multicolor_register_ext(struct device *parent,
- struct led_classdev_mc *mcled_cdev,
- struct led_init_data *init_data)
- {
- struct led_classdev *led_cdev;
- if (!mcled_cdev)
- return -EINVAL;
- if (mcled_cdev->num_colors <= 0)
- return -EINVAL;
- if (mcled_cdev->num_colors > LED_COLOR_ID_MAX)
- return -EINVAL;
- led_cdev = &mcled_cdev->led_cdev;
- led_cdev->flags |= LED_MULTI_COLOR;
- mcled_cdev->led_cdev.groups = led_multicolor_groups;
- return led_classdev_register_ext(parent, led_cdev, init_data);
- }
- EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
- void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
- {
- if (!mcled_cdev)
- return;
- led_classdev_unregister(&mcled_cdev->led_cdev);
- }
- EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
- static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
- {
- led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
- }
- int devm_led_classdev_multicolor_register_ext(struct device *parent,
- struct led_classdev_mc *mcled_cdev,
- struct led_init_data *init_data)
- {
- struct led_classdev_mc **dr;
- int ret;
- dr = devres_alloc(devm_led_classdev_multicolor_release,
- sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
- ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
- init_data);
- if (ret) {
- devres_free(dr);
- return ret;
- }
- *dr = mcled_cdev;
- devres_add(parent, dr);
- return 0;
- }
- EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
- static int devm_led_classdev_multicolor_match(struct device *dev,
- void *res, void *data)
- {
- struct led_classdev_mc **p = res;
- if (WARN_ON(!p || !*p))
- return 0;
- return *p == data;
- }
- void devm_led_classdev_multicolor_unregister(struct device *dev,
- struct led_classdev_mc *mcled_cdev)
- {
- WARN_ON(devres_release(dev,
- devm_led_classdev_multicolor_release,
- devm_led_classdev_multicolor_match, mcled_cdev));
- }
- EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
- MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
- MODULE_DESCRIPTION("Multicolor LED class interface");
- MODULE_LICENSE("GPL v2");
|