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