xref: /linux/drivers/leds/leds-max77705.c (revision 1260ed77798502de9c98020040d2995008de10cc)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Based on leds-max77650 driver
4  *
5  * LED driver for MAXIM 77705 PMIC.
6  * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.org>
7  */
8 
9 #include <linux/i2c.h>
10 #include <linux/led-class-multicolor.h>
11 #include <linux/leds.h>
12 #include <linux/mfd/max77705-private.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/regmap.h>
16 
17 #define MAX77705_LED_NUM_LEDS			4
18 #define MAX77705_LED_EN_MASK			GENMASK(1, 0)
19 #define MAX77705_LED_MAX_BRIGHTNESS		0xff
20 #define MAX77705_LED_EN_SHIFT(reg)		(reg * MAX77705_RGBLED_EN_WIDTH)
21 #define MAX77705_LED_REG_BRIGHTNESS(reg)	(reg + MAX77705_RGBLED_REG_LED0BRT)
22 
23 struct max77705_led {
24 	struct led_classdev cdev;
25 	struct led_classdev_mc mcdev;
26 	struct regmap *regmap;
27 
28 	struct mc_subled *subled_info;
29 };
30 
31 static const struct regmap_config max77705_leds_regmap_config = {
32 	.reg_base = MAX77705_RGBLED_REG_BASE,
33 	.reg_bits = 8,
34 	.val_bits = 8,
35 	.max_register = MAX77705_LED_REG_END,
36 };
37 
38 static int max77705_rgb_blink(struct led_classdev *cdev,
39 				unsigned long *delay_on,
40 				unsigned long *delay_off)
41 {
42 	struct max77705_led *led = container_of(cdev, struct max77705_led, cdev);
43 	int value, on_value, off_value;
44 
45 	if (*delay_on < MAX77705_RGB_DELAY_100_STEP)
46 		on_value = 0;
47 	else if (*delay_on < MAX77705_RGB_DELAY_100_STEP_LIM)
48 		on_value = *delay_on / MAX77705_RGB_DELAY_100_STEP - 1;
49 	else if (*delay_on < MAX77705_RGB_DELAY_250_STEP_LIM)
50 		on_value = (*delay_on - MAX77705_RGB_DELAY_100_STEP_LIM) /
51 				MAX77705_RGB_DELAY_250_STEP +
52 				MAX77705_RGB_DELAY_100_STEP_COUNT;
53 	else
54 		on_value = 15;
55 
56 	on_value <<= 4;
57 
58 	if (*delay_off < 1)
59 		off_value = 0;
60 	else if (*delay_off < MAX77705_RGB_DELAY_500_STEP)
61 		off_value = 1;
62 	else if (*delay_off < MAX77705_RGB_DELAY_500_STEP_LIM)
63 		off_value = *delay_off / MAX77705_RGB_DELAY_500_STEP;
64 	else if (*delay_off < MAX77705_RGB_DELAY_1000_STEP_LIM)
65 		off_value = (*delay_off - MAX77705_RGB_DELAY_1000_STEP_LIM) /
66 				MAX77705_RGB_DELAY_1000_STEP +
67 				MAX77705_RGB_DELAY_500_STEP_COUNT;
68 	else if (*delay_off < MAX77705_RGB_DELAY_2000_STEP_LIM)
69 		off_value = (*delay_off - MAX77705_RGB_DELAY_2000_STEP_LIM) /
70 				MAX77705_RGB_DELAY_2000_STEP +
71 				MAX77705_RGB_DELAY_1000_STEP_COUNT;
72 	else
73 		off_value = 15;
74 
75 	value = on_value | off_value;
76 	return regmap_write(led->regmap, MAX77705_RGBLED_REG_LEDBLNK, value);
77 }
78 
79 static int max77705_led_brightness_set(struct regmap *regmap, struct mc_subled *subled,
80 				int num_colors)
81 {
82 	int ret;
83 
84 	for (int i = 0; i < num_colors; i++) {
85 		unsigned int channel, brightness;
86 
87 		channel = subled[i].channel;
88 		brightness = subled[i].brightness;
89 
90 		if (brightness == LED_OFF) {
91 			/* Flash OFF */
92 			ret = regmap_update_bits(regmap,
93 					MAX77705_RGBLED_REG_LEDEN,
94 					MAX77705_LED_EN_MASK << MAX77705_LED_EN_SHIFT(channel), 0);
95 		} else {
96 			/* Set current */
97 			ret = regmap_write(regmap, MAX77705_LED_REG_BRIGHTNESS(channel),
98 					brightness);
99 			if (ret < 0)
100 				return ret;
101 
102 			ret = regmap_update_bits(regmap,
103 					MAX77705_RGBLED_REG_LEDEN,
104 					LED_ON << MAX77705_LED_EN_SHIFT(channel),
105 					MAX77705_LED_EN_MASK << MAX77705_LED_EN_SHIFT(channel));
106 		}
107 	}
108 
109 	return ret;
110 }
111 
112 static int max77705_led_brightness_set_single(struct led_classdev *cdev,
113 					enum led_brightness brightness)
114 {
115 	struct max77705_led *led = container_of(cdev, struct max77705_led, cdev);
116 
117 	led->subled_info->brightness = brightness;
118 
119 	return max77705_led_brightness_set(led->regmap, led->subled_info, 1);
120 }
121 
122 static int max77705_led_brightness_set_multi(struct led_classdev *cdev,
123 					enum led_brightness brightness)
124 {
125 	struct led_classdev_mc *mcdev = lcdev_to_mccdev(cdev);
126 	struct max77705_led *led = container_of(mcdev, struct max77705_led, mcdev);
127 
128 	led_mc_calc_color_components(mcdev, brightness);
129 
130 	return max77705_led_brightness_set(led->regmap, led->mcdev.subled_info, mcdev->num_colors);
131 }
132 
133 static int max77705_parse_subled(struct device *dev, struct fwnode_handle *np,
134 				struct mc_subled *info)
135 {
136 	u32 color = LED_COLOR_ID_GREEN;
137 	u32 reg;
138 	int ret;
139 
140 	ret = fwnode_property_read_u32(np, "reg", &reg);
141 	if (ret || !reg || reg >= MAX77705_LED_NUM_LEDS)
142 		return dev_err_probe(dev, -EINVAL, "invalid \"reg\" of %pOFn\n", np);
143 
144 	info->channel = reg;
145 
146 	ret = fwnode_property_read_u32(np, "color", &color);
147 	if (ret < 0 && ret != -EINVAL)
148 		return dev_err_probe(dev, ret,
149 				     "failed to parse \"color\" of %pOF\n", np);
150 
151 	info->color_index = color;
152 
153 	return 0;
154 }
155 
156 static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fwnode_handle *np)
157 {
158 	int ret, i = 0;
159 	unsigned int color, reg;
160 	struct max77705_led *led;
161 	struct led_classdev *cdev;
162 	struct mc_subled *info;
163 	struct fwnode_handle *child;
164 	struct led_init_data init_data = {};
165 
166 	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
167 	if (!led)
168 		return -ENOMEM;
169 
170 	ret = fwnode_property_read_u32(np, "color", &color);
171 	if (ret < 0 && ret != -EINVAL)
172 		return dev_err_probe(dev, ret,
173 			      "failed to parse \"color\" of %pOF\n", np);
174 
175 	led->regmap = regmap;
176 	init_data.fwnode = np;
177 
178 	if (color == LED_COLOR_ID_RGB) {
179 		int num_channels = of_get_available_child_count(to_of_node(np));
180 
181 		ret = fwnode_property_read_u32(np, "reg", &reg);
182 		if (ret || reg >= MAX77705_LED_NUM_LEDS)
183 			ret = -EINVAL;
184 
185 		info = devm_kcalloc(dev, num_channels, sizeof(*info), GFP_KERNEL);
186 		if (!info)
187 			return -ENOMEM;
188 
189 		cdev = &led->mcdev.led_cdev;
190 		cdev->max_brightness = MAX77705_LED_MAX_BRIGHTNESS;
191 		cdev->brightness_set_blocking = max77705_led_brightness_set_multi;
192 		cdev->blink_set = max77705_rgb_blink;
193 
194 		fwnode_for_each_available_child_node(np, child) {
195 			ret = max77705_parse_subled(dev, child, &info[i]);
196 			if (ret < 0)
197 				return ret;
198 
199 			info[i].intensity = 0;
200 			i++;
201 		}
202 
203 		led->mcdev.subled_info = info;
204 		led->mcdev.num_colors = num_channels;
205 		led->cdev = *cdev;
206 
207 		ret = devm_led_classdev_multicolor_register_ext(dev, &led->mcdev, &init_data);
208 		if (ret)
209 			return ret;
210 
211 		ret = max77705_led_brightness_set_multi(&led->cdev, LED_OFF);
212 		if (ret)
213 			return ret;
214 	} else {
215 		info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
216 		if (!info)
217 			return -ENOMEM;
218 
219 		max77705_parse_subled(dev, np, info);
220 
221 		led->subled_info = info;
222 		led->cdev.brightness_set_blocking = max77705_led_brightness_set_single;
223 		led->cdev.blink_set = max77705_rgb_blink;
224 		led->cdev.max_brightness = MAX77705_LED_MAX_BRIGHTNESS;
225 
226 		ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
227 		if (ret)
228 			return ret;
229 
230 		ret = max77705_led_brightness_set_single(&led->cdev, LED_OFF);
231 		if (ret)
232 			return ret;
233 	}
234 
235 	return 0;
236 }
237 
238 static int max77705_led_probe(struct platform_device *pdev)
239 {
240 	struct device *dev = &pdev->dev;
241 	struct i2c_client *i2c = to_i2c_client(pdev->dev.parent);
242 	struct regmap *regmap;
243 	int ret;
244 
245 	regmap = devm_regmap_init_i2c(i2c, &max77705_leds_regmap_config);
246 	if (IS_ERR(regmap))
247 		return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register LEDs regmap\n");
248 
249 	device_for_each_child_node_scoped(dev, child) {
250 		ret = max77705_add_led(dev, regmap, child);
251 		if (ret)
252 			return ret;
253 	}
254 
255 	return 0;
256 }
257 
258 static const struct of_device_id max77705_led_of_match[] = {
259 	{ .compatible = "maxim,max77705-rgb" },
260 	{ }
261 };
262 MODULE_DEVICE_TABLE(of, max77705_led_of_match);
263 
264 static struct platform_driver max77705_led_driver = {
265 	.driver = {
266 		.name = "max77705-led",
267 		.of_match_table = max77705_led_of_match,
268 	},
269 	.probe = max77705_led_probe,
270 };
271 module_platform_driver(max77705_led_driver);
272 
273 MODULE_DESCRIPTION("Maxim MAX77705 LED driver");
274 MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
275 MODULE_LICENSE("GPL");
276