linux/drivers/leds/led-class-multicolor.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// LED Multicolor class interface
   3// Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/
   4// Author: Dan Murphy <dmurphy@ti.com>
   5
   6#include <linux/device.h>
   7#include <linux/init.h>
   8#include <linux/led-class-multicolor.h>
   9#include <linux/module.h>
  10#include <linux/slab.h>
  11#include <linux/uaccess.h>
  12
  13#include "leds.h"
  14
  15int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
  16                                 enum led_brightness brightness)
  17{
  18        struct led_classdev *led_cdev = &mcled_cdev->led_cdev;
  19        int i;
  20
  21        for (i = 0; i < mcled_cdev->num_colors; i++)
  22                mcled_cdev->subled_info[i].brightness = brightness *
  23                                        mcled_cdev->subled_info[i].intensity /
  24                                        led_cdev->max_brightness;
  25
  26        return 0;
  27}
  28EXPORT_SYMBOL_GPL(led_mc_calc_color_components);
  29
  30static ssize_t multi_intensity_store(struct device *dev,
  31                                struct device_attribute *intensity_attr,
  32                                const char *buf, size_t size)
  33{
  34        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  35        struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
  36        int nrchars, offset = 0;
  37        int intensity_value[LED_COLOR_ID_MAX];
  38        int i;
  39        ssize_t ret;
  40
  41        mutex_lock(&led_cdev->led_access);
  42
  43        for (i = 0; i < mcled_cdev->num_colors; i++) {
  44                ret = sscanf(buf + offset, "%i%n",
  45                             &intensity_value[i], &nrchars);
  46                if (ret != 1) {
  47                        ret = -EINVAL;
  48                        goto err_out;
  49                }
  50                offset += nrchars;
  51        }
  52
  53        offset++;
  54        if (offset < size) {
  55                ret = -EINVAL;
  56                goto err_out;
  57        }
  58
  59        for (i = 0; i < mcled_cdev->num_colors; i++)
  60                mcled_cdev->subled_info[i].intensity = intensity_value[i];
  61
  62        led_set_brightness(led_cdev, led_cdev->brightness);
  63        ret = size;
  64err_out:
  65        mutex_unlock(&led_cdev->led_access);
  66        return ret;
  67}
  68
  69static ssize_t multi_intensity_show(struct device *dev,
  70                              struct device_attribute *intensity_attr,
  71                              char *buf)
  72{
  73        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  74        struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
  75        int len = 0;
  76        int i;
  77
  78        for (i = 0; i < mcled_cdev->num_colors; i++) {
  79                len += sprintf(buf + len, "%d",
  80                               mcled_cdev->subled_info[i].intensity);
  81                if (i < mcled_cdev->num_colors - 1)
  82                        len += sprintf(buf + len, " ");
  83        }
  84
  85        buf[len++] = '\n';
  86        return len;
  87}
  88static DEVICE_ATTR_RW(multi_intensity);
  89
  90static ssize_t multi_index_show(struct device *dev,
  91                              struct device_attribute *multi_index_attr,
  92                              char *buf)
  93{
  94        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  95        struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
  96        int len = 0;
  97        int index;
  98        int i;
  99
 100        for (i = 0; i < mcled_cdev->num_colors; i++) {
 101                index = mcled_cdev->subled_info[i].color_index;
 102                len += sprintf(buf + len, "%s", led_colors[index]);
 103                if (i < mcled_cdev->num_colors - 1)
 104                        len += sprintf(buf + len, " ");
 105        }
 106
 107        buf[len++] = '\n';
 108        return len;
 109}
 110static DEVICE_ATTR_RO(multi_index);
 111
 112static struct attribute *led_multicolor_attrs[] = {
 113        &dev_attr_multi_intensity.attr,
 114        &dev_attr_multi_index.attr,
 115        NULL,
 116};
 117ATTRIBUTE_GROUPS(led_multicolor);
 118
 119int led_classdev_multicolor_register_ext(struct device *parent,
 120                                     struct led_classdev_mc *mcled_cdev,
 121                                     struct led_init_data *init_data)
 122{
 123        struct led_classdev *led_cdev;
 124
 125        if (!mcled_cdev)
 126                return -EINVAL;
 127
 128        if (mcled_cdev->num_colors <= 0)
 129                return -EINVAL;
 130
 131        if (mcled_cdev->num_colors > LED_COLOR_ID_MAX)
 132                return -EINVAL;
 133
 134        led_cdev = &mcled_cdev->led_cdev;
 135        mcled_cdev->led_cdev.groups = led_multicolor_groups;
 136
 137        return led_classdev_register_ext(parent, led_cdev, init_data);
 138}
 139EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
 140
 141void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
 142{
 143        if (!mcled_cdev)
 144                return;
 145
 146        led_classdev_unregister(&mcled_cdev->led_cdev);
 147}
 148EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
 149
 150static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
 151{
 152        led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
 153}
 154
 155int devm_led_classdev_multicolor_register_ext(struct device *parent,
 156                                             struct led_classdev_mc *mcled_cdev,
 157                                             struct led_init_data *init_data)
 158{
 159        struct led_classdev_mc **dr;
 160        int ret;
 161
 162        dr = devres_alloc(devm_led_classdev_multicolor_release,
 163                          sizeof(*dr), GFP_KERNEL);
 164        if (!dr)
 165                return -ENOMEM;
 166
 167        ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
 168                                                   init_data);
 169        if (ret) {
 170                devres_free(dr);
 171                return ret;
 172        }
 173
 174        *dr = mcled_cdev;
 175        devres_add(parent, dr);
 176
 177        return 0;
 178}
 179EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
 180
 181static int devm_led_classdev_multicolor_match(struct device *dev,
 182                                              void *res, void *data)
 183{
 184        struct led_classdev_mc **p = res;
 185
 186        if (WARN_ON(!p || !*p))
 187                return 0;
 188
 189        return *p == data;
 190}
 191
 192void devm_led_classdev_multicolor_unregister(struct device *dev,
 193                                             struct led_classdev_mc *mcled_cdev)
 194{
 195        WARN_ON(devres_release(dev,
 196                               devm_led_classdev_multicolor_release,
 197                               devm_led_classdev_multicolor_match, mcled_cdev));
 198}
 199EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
 200
 201MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
 202MODULE_DESCRIPTION("Multicolor LED class interface");
 203MODULE_LICENSE("GPL v2");
 204