root/drivers/leds/leds-sc27xx-bltc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sc27xx_led_init
  2. sc27xx_led_get_offset
  3. sc27xx_led_enable
  4. sc27xx_led_disable
  5. sc27xx_led_set
  6. sc27xx_led_clamp_align_delta_t
  7. sc27xx_led_pattern_clear
  8. sc27xx_led_pattern_set
  9. sc27xx_led_register
  10. sc27xx_led_probe
  11. sc27xx_led_remove

   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, *child;
 280         struct sc27xx_led_priv *priv;
 281         u32 base, count, reg;
 282         int err;
 283 
 284         count = of_get_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_child_of_node(np, child) {
 309                 err = of_property_read_u32(child, "reg", &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 
 333 static 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 
 341 static const struct of_device_id sc27xx_led_of_match[] = {
 342         { .compatible = "sprd,sc2731-bltc", },
 343         { }
 344 };
 345 MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
 346 
 347 static 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 
 356 module_platform_driver(sc27xx_led_driver);
 357 
 358 MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
 359 MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
 360 MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
 361 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */