]> Git Repo - J-linux.git/blob - drivers/leds/led-class-multicolor.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / leds / led-class-multicolor.c
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 <[email protected]>
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 int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
15                                  enum led_brightness brightness)
16 {
17         struct led_classdev *led_cdev = &mcled_cdev->led_cdev;
18         int i;
19
20         for (i = 0; i < mcled_cdev->num_colors; i++)
21                 mcled_cdev->subled_info[i].brightness =
22                         DIV_ROUND_CLOSEST(brightness *
23                                           mcled_cdev->subled_info[i].intensity,
24                                           led_cdev->max_brightness);
25
26         return 0;
27 }
28 EXPORT_SYMBOL_GPL(led_mc_calc_color_components);
29
30 static 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;
64 err_out:
65         mutex_unlock(&led_cdev->led_access);
66         return ret;
67 }
68
69 static 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 }
88 static DEVICE_ATTR_RW(multi_intensity);
89
90 static 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_get_color_name(index));
103                 if (i < mcled_cdev->num_colors - 1)
104                         len += sprintf(buf + len, " ");
105         }
106
107         buf[len++] = '\n';
108         return len;
109 }
110 static DEVICE_ATTR_RO(multi_index);
111
112 static struct attribute *led_multicolor_attrs[] = {
113         &dev_attr_multi_intensity.attr,
114         &dev_attr_multi_index.attr,
115         NULL,
116 };
117 ATTRIBUTE_GROUPS(led_multicolor);
118
119 int 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         led_cdev->flags |= LED_MULTI_COLOR;
136         mcled_cdev->led_cdev.groups = led_multicolor_groups;
137
138         return led_classdev_register_ext(parent, led_cdev, init_data);
139 }
140 EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
141
142 void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
143 {
144         if (!mcled_cdev)
145                 return;
146
147         led_classdev_unregister(&mcled_cdev->led_cdev);
148 }
149 EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
150
151 static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
152 {
153         led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
154 }
155
156 int devm_led_classdev_multicolor_register_ext(struct device *parent,
157                                              struct led_classdev_mc *mcled_cdev,
158                                              struct led_init_data *init_data)
159 {
160         struct led_classdev_mc **dr;
161         int ret;
162
163         dr = devres_alloc(devm_led_classdev_multicolor_release,
164                           sizeof(*dr), GFP_KERNEL);
165         if (!dr)
166                 return -ENOMEM;
167
168         ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
169                                                    init_data);
170         if (ret) {
171                 devres_free(dr);
172                 return ret;
173         }
174
175         *dr = mcled_cdev;
176         devres_add(parent, dr);
177
178         return 0;
179 }
180 EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
181
182 static int devm_led_classdev_multicolor_match(struct device *dev,
183                                               void *res, void *data)
184 {
185         struct led_classdev_mc **p = res;
186
187         if (WARN_ON(!p || !*p))
188                 return 0;
189
190         return *p == data;
191 }
192
193 void devm_led_classdev_multicolor_unregister(struct device *dev,
194                                              struct led_classdev_mc *mcled_cdev)
195 {
196         WARN_ON(devres_release(dev,
197                                devm_led_classdev_multicolor_release,
198                                devm_led_classdev_multicolor_match, mcled_cdev));
199 }
200 EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
201
202 MODULE_AUTHOR("Dan Murphy <[email protected]>");
203 MODULE_DESCRIPTION("Multicolor LED class interface");
204 MODULE_LICENSE("GPL v2");
This page took 0.038027 seconds and 4 git commands to generate.