leds-sc27xx-bltc.c (9138B)
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 47struct 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 55struct 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 65static 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 82static u32 sc27xx_led_get_offset(struct sc27xx_led *leds) 83{ 84 return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line; 85} 86 87static 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 107static 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 117static 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 134static 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 146static 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 171static 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 235out: 236 mutex_unlock(&leds->priv->lock); 237 238 return err; 239} 240 241static 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 276static 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 mutex_init(&priv->lock); 300 priv->base = base; 301 priv->regmap = dev_get_regmap(dev->parent, NULL); 302 if (!priv->regmap) { 303 err = -ENODEV; 304 dev_err(dev, "failed to get regmap: %d\n", err); 305 return err; 306 } 307 308 for_each_available_child_of_node(np, child) { 309 err = of_property_read_u32(child, "reg", ®); 310 if (err) { 311 of_node_put(child); 312 mutex_destroy(&priv->lock); 313 return err; 314 } 315 316 if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) { 317 of_node_put(child); 318 mutex_destroy(&priv->lock); 319 return -EINVAL; 320 } 321 322 priv->leds[reg].fwnode = of_fwnode_handle(child); 323 priv->leds[reg].active = true; 324 } 325 326 err = sc27xx_led_register(dev, priv); 327 if (err) 328 mutex_destroy(&priv->lock); 329 330 return err; 331} 332 333static int sc27xx_led_remove(struct platform_device *pdev) 334{ 335 struct sc27xx_led_priv *priv = platform_get_drvdata(pdev); 336 337 mutex_destroy(&priv->lock); 338 return 0; 339} 340 341static const struct of_device_id sc27xx_led_of_match[] = { 342 { .compatible = "sprd,sc2731-bltc", }, 343 { } 344}; 345MODULE_DEVICE_TABLE(of, sc27xx_led_of_match); 346 347static struct platform_driver sc27xx_led_driver = { 348 .driver = { 349 .name = "sprd-bltc", 350 .of_match_table = sc27xx_led_of_match, 351 }, 352 .probe = sc27xx_led_probe, 353 .remove = sc27xx_led_remove, 354}; 355 356module_platform_driver(sc27xx_led_driver); 357 358MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver"); 359MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>"); 360MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>"); 361MODULE_LICENSE("GPL v2");