1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Spreadtrum Communications Inc.
4 #include <linux/leds.h>
5 #include <linux/module.h>
7 #include <linux/platform_device.h>
8 #include <linux/regmap.h>
9 #include <uapi/linux/uleds.h>
11 /* PMIC global control register definition */
12 #define SC27XX_MODULE_EN0 0xc08
13 #define SC27XX_CLK_EN0 0xc18
14 #define SC27XX_RGB_CTRL 0xebc
16 #define SC27XX_BLTC_EN BIT(9)
17 #define SC27XX_RTC_EN BIT(7)
18 #define SC27XX_RGB_PD BIT(0)
20 /* Breathing light controller register definition */
21 #define SC27XX_LEDS_CTRL 0x00
22 #define SC27XX_LEDS_PRESCALE 0x04
23 #define SC27XX_LEDS_DUTY 0x08
24 #define SC27XX_LEDS_CURVE0 0x0c
25 #define SC27XX_LEDS_CURVE1 0x10
27 #define SC27XX_CTRL_SHIFT 4
28 #define SC27XX_LED_RUN BIT(0)
29 #define SC27XX_LED_TYPE BIT(1)
31 #define SC27XX_DUTY_SHIFT 8
32 #define SC27XX_DUTY_MASK GENMASK(15, 0)
33 #define SC27XX_MOD_MASK GENMASK(7, 0)
35 #define SC27XX_CURVE_SHIFT 8
36 #define SC27XX_CURVE_L_MASK GENMASK(7, 0)
37 #define SC27XX_CURVE_H_MASK GENMASK(15, 8)
39 #define SC27XX_LEDS_OFFSET 0x10
40 #define SC27XX_LEDS_MAX 3
41 #define SC27XX_LEDS_PATTERN_CNT 4
42 /* Stage duration step, in milliseconds */
43 #define SC27XX_LEDS_STEP 125
44 /* Minimum and maximum duration, in milliseconds */
45 #define SC27XX_DELTA_T_MIN SC27XX_LEDS_STEP
46 #define SC27XX_DELTA_T_MAX (SC27XX_LEDS_STEP * 255)
49 char name
[LED_MAX_NAME_SIZE
];
50 struct led_classdev ldev
;
51 struct sc27xx_led_priv
*priv
;
56 struct sc27xx_led_priv
{
57 struct sc27xx_led leds
[SC27XX_LEDS_MAX
];
58 struct regmap
*regmap
;
63 #define to_sc27xx_led(ldev) \
64 container_of(ldev, struct sc27xx_led, ldev)
66 static int sc27xx_led_init(struct regmap
*regmap
)
70 err
= regmap_update_bits(regmap
, SC27XX_MODULE_EN0
, SC27XX_BLTC_EN
,
75 err
= regmap_update_bits(regmap
, SC27XX_CLK_EN0
, SC27XX_RTC_EN
,
80 return regmap_update_bits(regmap
, SC27XX_RGB_CTRL
, SC27XX_RGB_PD
, 0);
83 static u32
sc27xx_led_get_offset(struct sc27xx_led
*leds
)
85 return leds
->priv
->base
+ SC27XX_LEDS_OFFSET
* leds
->line
;
88 static int sc27xx_led_enable(struct sc27xx_led
*leds
, enum led_brightness value
)
90 u32 base
= sc27xx_led_get_offset(leds
);
91 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
92 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
93 struct regmap
*regmap
= leds
->priv
->regmap
;
96 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_DUTY
,
98 (value
<< SC27XX_DUTY_SHIFT
) |
103 return regmap_update_bits(regmap
, ctrl_base
,
104 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
,
105 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
);
108 static int sc27xx_led_disable(struct sc27xx_led
*leds
)
110 struct regmap
*regmap
= leds
->priv
->regmap
;
111 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
112 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
114 return regmap_update_bits(regmap
, ctrl_base
,
115 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
, 0);
118 static int sc27xx_led_set(struct led_classdev
*ldev
, enum led_brightness value
)
120 struct sc27xx_led
*leds
= to_sc27xx_led(ldev
);
123 mutex_lock(&leds
->priv
->lock
);
125 if (value
== LED_OFF
)
126 err
= sc27xx_led_disable(leds
);
128 err
= sc27xx_led_enable(leds
, value
);
130 mutex_unlock(&leds
->priv
->lock
);
135 static void sc27xx_led_clamp_align_delta_t(u32
*delta_t
)
137 u32 v
, offset
, t
= *delta_t
;
139 v
= t
+ SC27XX_LEDS_STEP
/ 2;
140 v
= clamp_t(u32
, v
, SC27XX_DELTA_T_MIN
, SC27XX_DELTA_T_MAX
);
141 offset
= v
- SC27XX_DELTA_T_MIN
;
142 offset
= SC27XX_LEDS_STEP
* (offset
/ SC27XX_LEDS_STEP
);
144 *delta_t
= SC27XX_DELTA_T_MIN
+ offset
;
147 static int sc27xx_led_pattern_clear(struct led_classdev
*ldev
)
149 struct sc27xx_led
*leds
= to_sc27xx_led(ldev
);
150 struct regmap
*regmap
= leds
->priv
->regmap
;
151 u32 base
= sc27xx_led_get_offset(leds
);
152 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
153 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
156 mutex_lock(&leds
->priv
->lock
);
158 /* Reset the rise, high, fall and low time to zero. */
159 regmap_write(regmap
, base
+ SC27XX_LEDS_CURVE0
, 0);
160 regmap_write(regmap
, base
+ SC27XX_LEDS_CURVE1
, 0);
162 err
= regmap_update_bits(regmap
, ctrl_base
,
163 (SC27XX_LED_RUN
| SC27XX_LED_TYPE
) << ctrl_shift
, 0);
165 ldev
->brightness
= LED_OFF
;
167 mutex_unlock(&leds
->priv
->lock
);
172 static int sc27xx_led_pattern_set(struct led_classdev
*ldev
,
173 struct led_pattern
*pattern
,
176 struct sc27xx_led
*leds
= to_sc27xx_led(ldev
);
177 u32 base
= sc27xx_led_get_offset(leds
);
178 u32 ctrl_base
= leds
->priv
->base
+ SC27XX_LEDS_CTRL
;
179 u8 ctrl_shift
= SC27XX_CTRL_SHIFT
* leds
->line
;
180 struct regmap
*regmap
= leds
->priv
->regmap
;
184 * Must contain 4 tuples to configure the rise time, high time, fall
185 * time and low time to enable the breathing mode.
187 if (len
!= SC27XX_LEDS_PATTERN_CNT
)
190 mutex_lock(&leds
->priv
->lock
);
192 sc27xx_led_clamp_align_delta_t(&pattern
[0].delta_t
);
193 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_CURVE0
,
195 pattern
[0].delta_t
/ SC27XX_LEDS_STEP
);
199 sc27xx_led_clamp_align_delta_t(&pattern
[1].delta_t
);
200 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_CURVE1
,
202 pattern
[1].delta_t
/ SC27XX_LEDS_STEP
);
206 sc27xx_led_clamp_align_delta_t(&pattern
[2].delta_t
);
207 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_CURVE0
,
209 (pattern
[2].delta_t
/ SC27XX_LEDS_STEP
) <<
214 sc27xx_led_clamp_align_delta_t(&pattern
[3].delta_t
);
215 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_CURVE1
,
217 (pattern
[3].delta_t
/ SC27XX_LEDS_STEP
) <<
222 err
= regmap_update_bits(regmap
, base
+ SC27XX_LEDS_DUTY
,
224 (pattern
[1].brightness
<< SC27XX_DUTY_SHIFT
) |
229 /* Enable the LED breathing mode */
230 err
= regmap_update_bits(regmap
, ctrl_base
,
231 SC27XX_LED_RUN
<< ctrl_shift
,
232 SC27XX_LED_RUN
<< ctrl_shift
);
234 ldev
->brightness
= pattern
[1].brightness
;
237 mutex_unlock(&leds
->priv
->lock
);
242 static int sc27xx_led_register(struct device
*dev
, struct sc27xx_led_priv
*priv
)
246 err
= sc27xx_led_init(priv
->regmap
);
250 for (i
= 0; i
< SC27XX_LEDS_MAX
; i
++) {
251 struct sc27xx_led
*led
= &priv
->leds
[i
];
258 led
->ldev
.name
= led
->name
;
259 led
->ldev
.brightness_set_blocking
= sc27xx_led_set
;
260 led
->ldev
.pattern_set
= sc27xx_led_pattern_set
;
261 led
->ldev
.pattern_clear
= sc27xx_led_pattern_clear
;
262 led
->ldev
.default_trigger
= "pattern";
264 err
= devm_led_classdev_register(dev
, &led
->ldev
);
272 static int sc27xx_led_probe(struct platform_device
*pdev
)
274 struct device
*dev
= &pdev
->dev
;
275 struct device_node
*np
= dev
->of_node
, *child
;
276 struct sc27xx_led_priv
*priv
;
278 u32 base
, count
, reg
;
281 count
= of_get_child_count(np
);
282 if (!count
|| count
> SC27XX_LEDS_MAX
)
285 err
= of_property_read_u32(np
, "reg", &base
);
287 dev_err(dev
, "fail to get reg of property\n");
291 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
295 platform_set_drvdata(pdev
, priv
);
296 mutex_init(&priv
->lock
);
298 priv
->regmap
= dev_get_regmap(dev
->parent
, NULL
);
301 dev_err(dev
, "failed to get regmap: %d\n", err
);
305 for_each_child_of_node(np
, child
) {
306 err
= of_property_read_u32(child
, "reg", ®
);
309 mutex_destroy(&priv
->lock
);
313 if (reg
>= SC27XX_LEDS_MAX
|| priv
->leds
[reg
].active
) {
315 mutex_destroy(&priv
->lock
);
319 priv
->leds
[reg
].active
= true;
321 err
= of_property_read_string(child
, "label", &str
);
323 snprintf(priv
->leds
[reg
].name
, LED_MAX_NAME_SIZE
,
326 snprintf(priv
->leds
[reg
].name
, LED_MAX_NAME_SIZE
,
330 err
= sc27xx_led_register(dev
, priv
);
332 mutex_destroy(&priv
->lock
);
337 static int sc27xx_led_remove(struct platform_device
*pdev
)
339 struct sc27xx_led_priv
*priv
= platform_get_drvdata(pdev
);
341 mutex_destroy(&priv
->lock
);
345 static const struct of_device_id sc27xx_led_of_match
[] = {
346 { .compatible
= "sprd,sc2731-bltc", },
349 MODULE_DEVICE_TABLE(of
, sc27xx_led_of_match
);
351 static struct platform_driver sc27xx_led_driver
= {
354 .of_match_table
= sc27xx_led_of_match
,
356 .probe
= sc27xx_led_probe
,
357 .remove
= sc27xx_led_remove
,
360 module_platform_driver(sc27xx_led_driver
);
362 MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
363 MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
364 MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
365 MODULE_LICENSE("GPL v2");