Loading...
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");
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/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_colors[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 mcled_cdev->led_cdev.groups = led_multicolor_groups;
138
139 return led_classdev_register_ext(parent, led_cdev, init_data);
140}
141EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
142
143void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
144{
145 if (!mcled_cdev)
146 return;
147
148 led_classdev_unregister(&mcled_cdev->led_cdev);
149}
150EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
151
152static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
153{
154 led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
155}
156
157int devm_led_classdev_multicolor_register_ext(struct device *parent,
158 struct led_classdev_mc *mcled_cdev,
159 struct led_init_data *init_data)
160{
161 struct led_classdev_mc **dr;
162 int ret;
163
164 dr = devres_alloc(devm_led_classdev_multicolor_release,
165 sizeof(*dr), GFP_KERNEL);
166 if (!dr)
167 return -ENOMEM;
168
169 ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
170 init_data);
171 if (ret) {
172 devres_free(dr);
173 return ret;
174 }
175
176 *dr = mcled_cdev;
177 devres_add(parent, dr);
178
179 return 0;
180}
181EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
182
183static int devm_led_classdev_multicolor_match(struct device *dev,
184 void *res, void *data)
185{
186 struct led_classdev_mc **p = res;
187
188 if (WARN_ON(!p || !*p))
189 return 0;
190
191 return *p == data;
192}
193
194void devm_led_classdev_multicolor_unregister(struct device *dev,
195 struct led_classdev_mc *mcled_cdev)
196{
197 WARN_ON(devres_release(dev,
198 devm_led_classdev_multicolor_release,
199 devm_led_classdev_multicolor_match, mcled_cdev));
200}
201EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
202
203MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
204MODULE_DESCRIPTION("Multicolor LED class interface");
205MODULE_LICENSE("GPL v2");