• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
led_mc_calc_color_components(struct led_classdev_mc * mcled_cdev,enum led_brightness brightness)16 int 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 }
30 EXPORT_SYMBOL_GPL(led_mc_calc_color_components);
31 
multi_intensity_store(struct device * dev,struct device_attribute * intensity_attr,const char * buf,size_t size)32 static 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 	if (!test_bit(LED_BLINK_SW, &led_cdev->work_flags))
65 		led_set_brightness(led_cdev, led_cdev->brightness);
66 	ret = size;
67 err_out:
68 	mutex_unlock(&led_cdev->led_access);
69 	return ret;
70 }
71 
multi_intensity_show(struct device * dev,struct device_attribute * intensity_attr,char * buf)72 static ssize_t multi_intensity_show(struct device *dev,
73 			      struct device_attribute *intensity_attr,
74 			      char *buf)
75 {
76 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
77 	struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
78 	int len = 0;
79 	int i;
80 
81 	for (i = 0; i < mcled_cdev->num_colors; i++) {
82 		len += sprintf(buf + len, "%d",
83 			       mcled_cdev->subled_info[i].intensity);
84 		if (i < mcled_cdev->num_colors - 1)
85 			len += sprintf(buf + len, " ");
86 	}
87 
88 	buf[len++] = '\n';
89 	return len;
90 }
91 static DEVICE_ATTR_RW(multi_intensity);
92 
multi_index_show(struct device * dev,struct device_attribute * multi_index_attr,char * buf)93 static ssize_t multi_index_show(struct device *dev,
94 			      struct device_attribute *multi_index_attr,
95 			      char *buf)
96 {
97 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
98 	struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
99 	int len = 0;
100 	int index;
101 	int i;
102 
103 	for (i = 0; i < mcled_cdev->num_colors; i++) {
104 		index = mcled_cdev->subled_info[i].color_index;
105 		len += sprintf(buf + len, "%s", led_get_color_name(index));
106 		if (i < mcled_cdev->num_colors - 1)
107 			len += sprintf(buf + len, " ");
108 	}
109 
110 	buf[len++] = '\n';
111 	return len;
112 }
113 static DEVICE_ATTR_RO(multi_index);
114 
115 static struct attribute *led_multicolor_attrs[] = {
116 	&dev_attr_multi_intensity.attr,
117 	&dev_attr_multi_index.attr,
118 	NULL,
119 };
120 ATTRIBUTE_GROUPS(led_multicolor);
121 
led_classdev_multicolor_register_ext(struct device * parent,struct led_classdev_mc * mcled_cdev,struct led_init_data * init_data)122 int led_classdev_multicolor_register_ext(struct device *parent,
123 				     struct led_classdev_mc *mcled_cdev,
124 				     struct led_init_data *init_data)
125 {
126 	struct led_classdev *led_cdev;
127 
128 	if (!mcled_cdev)
129 		return -EINVAL;
130 
131 	if (mcled_cdev->num_colors <= 0)
132 		return -EINVAL;
133 
134 	if (mcled_cdev->num_colors > LED_COLOR_ID_MAX)
135 		return -EINVAL;
136 
137 	led_cdev = &mcled_cdev->led_cdev;
138 	led_cdev->flags |= LED_MULTI_COLOR;
139 	mcled_cdev->led_cdev.groups = led_multicolor_groups;
140 
141 	return led_classdev_register_ext(parent, led_cdev, init_data);
142 }
143 EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
144 
led_classdev_multicolor_unregister(struct led_classdev_mc * mcled_cdev)145 void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
146 {
147 	if (!mcled_cdev)
148 		return;
149 
150 	led_classdev_unregister(&mcled_cdev->led_cdev);
151 }
152 EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
153 
devm_led_classdev_multicolor_release(struct device * dev,void * res)154 static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
155 {
156 	led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
157 }
158 
devm_led_classdev_multicolor_register_ext(struct device * parent,struct led_classdev_mc * mcled_cdev,struct led_init_data * init_data)159 int devm_led_classdev_multicolor_register_ext(struct device *parent,
160 					     struct led_classdev_mc *mcled_cdev,
161 					     struct led_init_data *init_data)
162 {
163 	struct led_classdev_mc **dr;
164 	int ret;
165 
166 	dr = devres_alloc(devm_led_classdev_multicolor_release,
167 			  sizeof(*dr), GFP_KERNEL);
168 	if (!dr)
169 		return -ENOMEM;
170 
171 	ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
172 						   init_data);
173 	if (ret) {
174 		devres_free(dr);
175 		return ret;
176 	}
177 
178 	*dr = mcled_cdev;
179 	devres_add(parent, dr);
180 
181 	return 0;
182 }
183 EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
184 
devm_led_classdev_multicolor_match(struct device * dev,void * res,void * data)185 static int devm_led_classdev_multicolor_match(struct device *dev,
186 					      void *res, void *data)
187 {
188 	struct led_classdev_mc **p = res;
189 
190 	if (WARN_ON(!p || !*p))
191 		return 0;
192 
193 	return *p == data;
194 }
195 
devm_led_classdev_multicolor_unregister(struct device * dev,struct led_classdev_mc * mcled_cdev)196 void devm_led_classdev_multicolor_unregister(struct device *dev,
197 					     struct led_classdev_mc *mcled_cdev)
198 {
199 	WARN_ON(devres_release(dev,
200 			       devm_led_classdev_multicolor_release,
201 			       devm_led_classdev_multicolor_match, mcled_cdev));
202 }
203 EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
204 
205 MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
206 MODULE_DESCRIPTION("Multicolor LED class interface");
207 MODULE_LICENSE("GPL v2");
208