Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // LED Multicolor class interface
0003 // Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/
0004 // Author: Dan Murphy <dmurphy@ti.com>
0005 
0006 #include <linux/device.h>
0007 #include <linux/init.h>
0008 #include <linux/led-class-multicolor.h>
0009 #include <linux/module.h>
0010 #include <linux/slab.h>
0011 #include <linux/uaccess.h>
0012 
0013 #include "leds.h"
0014 
0015 int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
0016                  enum led_brightness brightness)
0017 {
0018     struct led_classdev *led_cdev = &mcled_cdev->led_cdev;
0019     int i;
0020 
0021     for (i = 0; i < mcled_cdev->num_colors; i++)
0022         mcled_cdev->subled_info[i].brightness = brightness *
0023                     mcled_cdev->subled_info[i].intensity /
0024                     led_cdev->max_brightness;
0025 
0026     return 0;
0027 }
0028 EXPORT_SYMBOL_GPL(led_mc_calc_color_components);
0029 
0030 static ssize_t multi_intensity_store(struct device *dev,
0031                 struct device_attribute *intensity_attr,
0032                 const char *buf, size_t size)
0033 {
0034     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0035     struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
0036     int nrchars, offset = 0;
0037     int intensity_value[LED_COLOR_ID_MAX];
0038     int i;
0039     ssize_t ret;
0040 
0041     mutex_lock(&led_cdev->led_access);
0042 
0043     for (i = 0; i < mcled_cdev->num_colors; i++) {
0044         ret = sscanf(buf + offset, "%i%n",
0045                  &intensity_value[i], &nrchars);
0046         if (ret != 1) {
0047             ret = -EINVAL;
0048             goto err_out;
0049         }
0050         offset += nrchars;
0051     }
0052 
0053     offset++;
0054     if (offset < size) {
0055         ret = -EINVAL;
0056         goto err_out;
0057     }
0058 
0059     for (i = 0; i < mcled_cdev->num_colors; i++)
0060         mcled_cdev->subled_info[i].intensity = intensity_value[i];
0061 
0062     led_set_brightness(led_cdev, led_cdev->brightness);
0063     ret = size;
0064 err_out:
0065     mutex_unlock(&led_cdev->led_access);
0066     return ret;
0067 }
0068 
0069 static ssize_t multi_intensity_show(struct device *dev,
0070                   struct device_attribute *intensity_attr,
0071                   char *buf)
0072 {
0073     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0074     struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
0075     int len = 0;
0076     int i;
0077 
0078     for (i = 0; i < mcled_cdev->num_colors; i++) {
0079         len += sprintf(buf + len, "%d",
0080                    mcled_cdev->subled_info[i].intensity);
0081         if (i < mcled_cdev->num_colors - 1)
0082             len += sprintf(buf + len, " ");
0083     }
0084 
0085     buf[len++] = '\n';
0086     return len;
0087 }
0088 static DEVICE_ATTR_RW(multi_intensity);
0089 
0090 static ssize_t multi_index_show(struct device *dev,
0091                   struct device_attribute *multi_index_attr,
0092                   char *buf)
0093 {
0094     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0095     struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
0096     int len = 0;
0097     int index;
0098     int i;
0099 
0100     for (i = 0; i < mcled_cdev->num_colors; i++) {
0101         index = mcled_cdev->subled_info[i].color_index;
0102         len += sprintf(buf + len, "%s", led_colors[index]);
0103         if (i < mcled_cdev->num_colors - 1)
0104             len += sprintf(buf + len, " ");
0105     }
0106 
0107     buf[len++] = '\n';
0108     return len;
0109 }
0110 static DEVICE_ATTR_RO(multi_index);
0111 
0112 static struct attribute *led_multicolor_attrs[] = {
0113     &dev_attr_multi_intensity.attr,
0114     &dev_attr_multi_index.attr,
0115     NULL,
0116 };
0117 ATTRIBUTE_GROUPS(led_multicolor);
0118 
0119 int led_classdev_multicolor_register_ext(struct device *parent,
0120                      struct led_classdev_mc *mcled_cdev,
0121                      struct led_init_data *init_data)
0122 {
0123     struct led_classdev *led_cdev;
0124 
0125     if (!mcled_cdev)
0126         return -EINVAL;
0127 
0128     if (mcled_cdev->num_colors <= 0)
0129         return -EINVAL;
0130 
0131     if (mcled_cdev->num_colors > LED_COLOR_ID_MAX)
0132         return -EINVAL;
0133 
0134     led_cdev = &mcled_cdev->led_cdev;
0135     mcled_cdev->led_cdev.groups = led_multicolor_groups;
0136 
0137     return led_classdev_register_ext(parent, led_cdev, init_data);
0138 }
0139 EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
0140 
0141 void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
0142 {
0143     if (!mcled_cdev)
0144         return;
0145 
0146     led_classdev_unregister(&mcled_cdev->led_cdev);
0147 }
0148 EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
0149 
0150 static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
0151 {
0152     led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
0153 }
0154 
0155 int devm_led_classdev_multicolor_register_ext(struct device *parent,
0156                          struct led_classdev_mc *mcled_cdev,
0157                          struct led_init_data *init_data)
0158 {
0159     struct led_classdev_mc **dr;
0160     int ret;
0161 
0162     dr = devres_alloc(devm_led_classdev_multicolor_release,
0163               sizeof(*dr), GFP_KERNEL);
0164     if (!dr)
0165         return -ENOMEM;
0166 
0167     ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
0168                            init_data);
0169     if (ret) {
0170         devres_free(dr);
0171         return ret;
0172     }
0173 
0174     *dr = mcled_cdev;
0175     devres_add(parent, dr);
0176 
0177     return 0;
0178 }
0179 EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
0180 
0181 static int devm_led_classdev_multicolor_match(struct device *dev,
0182                           void *res, void *data)
0183 {
0184     struct led_classdev_mc **p = res;
0185 
0186     if (WARN_ON(!p || !*p))
0187         return 0;
0188 
0189     return *p == data;
0190 }
0191 
0192 void devm_led_classdev_multicolor_unregister(struct device *dev,
0193                          struct led_classdev_mc *mcled_cdev)
0194 {
0195     WARN_ON(devres_release(dev,
0196                    devm_led_classdev_multicolor_release,
0197                    devm_led_classdev_multicolor_match, mcled_cdev));
0198 }
0199 EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
0200 
0201 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
0202 MODULE_DESCRIPTION("Multicolor LED class interface");
0203 MODULE_LICENSE("GPL v2");