xref: /linux/drivers/leds/leds-sc27xx-bltc.c (revision d642ef71)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Spreadtrum Communications Inc.
3 
4 #include <linux/leds.h>
5 #include <linux/module.h>
6 #include <linux/of.h>
7 #include <linux/platform_device.h>
8 #include <linux/regmap.h>
9 
10 /* PMIC global control register definition */
11 #define SC27XX_MODULE_EN0	0xc08
12 #define SC27XX_CLK_EN0		0xc18
13 #define SC27XX_RGB_CTRL		0xebc
14 
15 #define SC27XX_BLTC_EN		BIT(9)
16 #define SC27XX_RTC_EN		BIT(7)
17 #define SC27XX_RGB_PD		BIT(0)
18 
19 /* Breathing light controller register definition */
20 #define SC27XX_LEDS_CTRL	0x00
21 #define SC27XX_LEDS_PRESCALE	0x04
22 #define SC27XX_LEDS_DUTY	0x08
23 #define SC27XX_LEDS_CURVE0	0x0c
24 #define SC27XX_LEDS_CURVE1	0x10
25 
26 #define SC27XX_CTRL_SHIFT	4
27 #define SC27XX_LED_RUN		BIT(0)
28 #define SC27XX_LED_TYPE		BIT(1)
29 
30 #define SC27XX_DUTY_SHIFT	8
31 #define SC27XX_DUTY_MASK	GENMASK(15, 0)
32 #define SC27XX_MOD_MASK		GENMASK(7, 0)
33 
34 #define SC27XX_CURVE_SHIFT	8
35 #define SC27XX_CURVE_L_MASK	GENMASK(7, 0)
36 #define SC27XX_CURVE_H_MASK	GENMASK(15, 8)
37 
38 #define SC27XX_LEDS_OFFSET	0x10
39 #define SC27XX_LEDS_MAX		3
40 #define SC27XX_LEDS_PATTERN_CNT	4
41 /* Stage duration step, in milliseconds */
42 #define SC27XX_LEDS_STEP	125
43 /* Minimum and maximum duration, in milliseconds */
44 #define SC27XX_DELTA_T_MIN	SC27XX_LEDS_STEP
45 #define SC27XX_DELTA_T_MAX	(SC27XX_LEDS_STEP * 255)
46 
47 struct sc27xx_led {
48 	struct fwnode_handle *fwnode;
49 	struct led_classdev ldev;
50 	struct sc27xx_led_priv *priv;
51 	u8 line;
52 	bool active;
53 };
54 
55 struct sc27xx_led_priv {
56 	struct sc27xx_led leds[SC27XX_LEDS_MAX];
57 	struct regmap *regmap;
58 	struct mutex lock;
59 	u32 base;
60 };
61 
62 #define to_sc27xx_led(ldev) \
63 	container_of(ldev, struct sc27xx_led, ldev)
64 
65 static int sc27xx_led_init(struct regmap *regmap)
66 {
67 	int err;
68 
69 	err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
70 				 SC27XX_BLTC_EN);
71 	if (err)
72 		return err;
73 
74 	err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
75 				 SC27XX_RTC_EN);
76 	if (err)
77 		return err;
78 
79 	return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
80 }
81 
82 static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
83 {
84 	return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
85 }
86 
87 static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
88 {
89 	u32 base = sc27xx_led_get_offset(leds);
90 	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
91 	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
92 	struct regmap *regmap = leds->priv->regmap;
93 	int err;
94 
95 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
96 				 SC27XX_DUTY_MASK,
97 				 (value << SC27XX_DUTY_SHIFT) |
98 				 SC27XX_MOD_MASK);
99 	if (err)
100 		return err;
101 
102 	return regmap_update_bits(regmap, ctrl_base,
103 			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift,
104 			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift);
105 }
106 
107 static int sc27xx_led_disable(struct sc27xx_led *leds)
108 {
109 	struct regmap *regmap = leds->priv->regmap;
110 	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
111 	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
112 
113 	return regmap_update_bits(regmap, ctrl_base,
114 			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
115 }
116 
117 static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
118 {
119 	struct sc27xx_led *leds = to_sc27xx_led(ldev);
120 	int err;
121 
122 	mutex_lock(&leds->priv->lock);
123 
124 	if (value == LED_OFF)
125 		err = sc27xx_led_disable(leds);
126 	else
127 		err = sc27xx_led_enable(leds, value);
128 
129 	mutex_unlock(&leds->priv->lock);
130 
131 	return err;
132 }
133 
134 static void sc27xx_led_clamp_align_delta_t(u32 *delta_t)
135 {
136 	u32 v, offset, t = *delta_t;
137 
138 	v = t + SC27XX_LEDS_STEP / 2;
139 	v = clamp_t(u32, v, SC27XX_DELTA_T_MIN, SC27XX_DELTA_T_MAX);
140 	offset = v - SC27XX_DELTA_T_MIN;
141 	offset = SC27XX_LEDS_STEP * (offset / SC27XX_LEDS_STEP);
142 
143 	*delta_t = SC27XX_DELTA_T_MIN + offset;
144 }
145 
146 static int sc27xx_led_pattern_clear(struct led_classdev *ldev)
147 {
148 	struct sc27xx_led *leds = to_sc27xx_led(ldev);
149 	struct regmap *regmap = leds->priv->regmap;
150 	u32 base = sc27xx_led_get_offset(leds);
151 	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
152 	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
153 	int err;
154 
155 	mutex_lock(&leds->priv->lock);
156 
157 	/* Reset the rise, high, fall and low time to zero. */
158 	regmap_write(regmap, base + SC27XX_LEDS_CURVE0, 0);
159 	regmap_write(regmap, base + SC27XX_LEDS_CURVE1, 0);
160 
161 	err = regmap_update_bits(regmap, ctrl_base,
162 			(SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
163 
164 	ldev->brightness = LED_OFF;
165 
166 	mutex_unlock(&leds->priv->lock);
167 
168 	return err;
169 }
170 
171 static int sc27xx_led_pattern_set(struct led_classdev *ldev,
172 				  struct led_pattern *pattern,
173 				  u32 len, int repeat)
174 {
175 	struct sc27xx_led *leds = to_sc27xx_led(ldev);
176 	u32 base = sc27xx_led_get_offset(leds);
177 	u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
178 	u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
179 	struct regmap *regmap = leds->priv->regmap;
180 	int err;
181 
182 	/*
183 	 * Must contain 4 tuples to configure the rise time, high time, fall
184 	 * time and low time to enable the breathing mode.
185 	 */
186 	if (len != SC27XX_LEDS_PATTERN_CNT)
187 		return -EINVAL;
188 
189 	mutex_lock(&leds->priv->lock);
190 
191 	sc27xx_led_clamp_align_delta_t(&pattern[0].delta_t);
192 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
193 				 SC27XX_CURVE_L_MASK,
194 				 pattern[0].delta_t / SC27XX_LEDS_STEP);
195 	if (err)
196 		goto out;
197 
198 	sc27xx_led_clamp_align_delta_t(&pattern[1].delta_t);
199 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
200 				 SC27XX_CURVE_L_MASK,
201 				 pattern[1].delta_t / SC27XX_LEDS_STEP);
202 	if (err)
203 		goto out;
204 
205 	sc27xx_led_clamp_align_delta_t(&pattern[2].delta_t);
206 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
207 				 SC27XX_CURVE_H_MASK,
208 				 (pattern[2].delta_t / SC27XX_LEDS_STEP) <<
209 				 SC27XX_CURVE_SHIFT);
210 	if (err)
211 		goto out;
212 
213 	sc27xx_led_clamp_align_delta_t(&pattern[3].delta_t);
214 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
215 				 SC27XX_CURVE_H_MASK,
216 				 (pattern[3].delta_t / SC27XX_LEDS_STEP) <<
217 				 SC27XX_CURVE_SHIFT);
218 	if (err)
219 		goto out;
220 
221 	err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
222 				 SC27XX_DUTY_MASK,
223 				 (pattern[1].brightness << SC27XX_DUTY_SHIFT) |
224 				 SC27XX_MOD_MASK);
225 	if (err)
226 		goto out;
227 
228 	/* Enable the LED breathing mode */
229 	err = regmap_update_bits(regmap, ctrl_base,
230 				 SC27XX_LED_RUN << ctrl_shift,
231 				 SC27XX_LED_RUN << ctrl_shift);
232 	if (!err)
233 		ldev->brightness = pattern[1].brightness;
234 
235 out:
236 	mutex_unlock(&leds->priv->lock);
237 
238 	return err;
239 }
240 
241 static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
242 {
243 	int i, err;
244 
245 	err = sc27xx_led_init(priv->regmap);
246 	if (err)
247 		return err;
248 
249 	for (i = 0; i < SC27XX_LEDS_MAX; i++) {
250 		struct sc27xx_led *led = &priv->leds[i];
251 		struct led_init_data init_data = {};
252 
253 		if (!led->active)
254 			continue;
255 
256 		led->line = i;
257 		led->priv = priv;
258 		led->ldev.brightness_set_blocking = sc27xx_led_set;
259 		led->ldev.pattern_set = sc27xx_led_pattern_set;
260 		led->ldev.pattern_clear = sc27xx_led_pattern_clear;
261 		led->ldev.default_trigger = "pattern";
262 
263 		init_data.fwnode = led->fwnode;
264 		init_data.devicename = "sc27xx";
265 		init_data.default_label = ":";
266 
267 		err = devm_led_classdev_register_ext(dev, &led->ldev,
268 						     &init_data);
269 		if (err)
270 			return err;
271 	}
272 
273 	return 0;
274 }
275 
276 static int sc27xx_led_probe(struct platform_device *pdev)
277 {
278 	struct device *dev = &pdev->dev;
279 	struct device_node *np = dev_of_node(dev), *child;
280 	struct sc27xx_led_priv *priv;
281 	u32 base, count, reg;
282 	int err;
283 
284 	count = of_get_available_child_count(np);
285 	if (!count || count > SC27XX_LEDS_MAX)
286 		return -EINVAL;
287 
288 	err = of_property_read_u32(np, "reg", &base);
289 	if (err) {
290 		dev_err(dev, "fail to get reg of property\n");
291 		return err;
292 	}
293 
294 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
295 	if (!priv)
296 		return -ENOMEM;
297 
298 	platform_set_drvdata(pdev, priv);
299 	priv->base = base;
300 	priv->regmap = dev_get_regmap(dev->parent, NULL);
301 	if (!priv->regmap) {
302 		err = -ENODEV;
303 		dev_err(dev, "failed to get regmap: %d\n", err);
304 		return err;
305 	}
306 
307 	for_each_available_child_of_node(np, child) {
308 		err = of_property_read_u32(child, "reg", &reg);
309 		if (err) {
310 			of_node_put(child);
311 			return err;
312 		}
313 
314 		if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
315 			of_node_put(child);
316 			return -EINVAL;
317 		}
318 
319 		priv->leds[reg].fwnode = of_fwnode_handle(child);
320 		priv->leds[reg].active = true;
321 	}
322 
323 	mutex_init(&priv->lock);
324 
325 	err = sc27xx_led_register(dev, priv);
326 	if (err)
327 		mutex_destroy(&priv->lock);
328 
329 	return err;
330 }
331 
332 static void sc27xx_led_remove(struct platform_device *pdev)
333 {
334 	struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
335 
336 	mutex_destroy(&priv->lock);
337 }
338 
339 static const struct of_device_id sc27xx_led_of_match[] = {
340 	{ .compatible = "sprd,sc2731-bltc", },
341 	{ }
342 };
343 MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
344 
345 static struct platform_driver sc27xx_led_driver = {
346 	.driver = {
347 		.name = "sprd-bltc",
348 		.of_match_table = sc27xx_led_of_match,
349 	},
350 	.probe = sc27xx_led_probe,
351 	.remove_new = sc27xx_led_remove,
352 };
353 
354 module_platform_driver(sc27xx_led_driver);
355 
356 MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
357 MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
358 MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
359 MODULE_LICENSE("GPL v2");
360