root/drivers/leds/leds-wm8350.c

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

DEFINITIONS

This source file includes following definitions.
  1. wm8350_led_enable
  2. wm8350_led_disable
  3. wm8350_led_set
  4. wm8350_led_shutdown
  5. wm8350_led_probe
  6. wm8350_led_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * LED driver for WM8350 driven LEDS.
   4  *
   5  * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
   6  */
   7 
   8 #include <linux/kernel.h>
   9 #include <linux/platform_device.h>
  10 #include <linux/leds.h>
  11 #include <linux/err.h>
  12 #include <linux/mfd/wm8350/pmic.h>
  13 #include <linux/regulator/consumer.h>
  14 #include <linux/slab.h>
  15 #include <linux/module.h>
  16 
  17 /* Microamps */
  18 static const int isink_cur[] = {
  19         4,
  20         5,
  21         6,
  22         7,
  23         8,
  24         10,
  25         11,
  26         14,
  27         16,
  28         19,
  29         23,
  30         27,
  31         32,
  32         39,
  33         46,
  34         54,
  35         65,
  36         77,
  37         92,
  38         109,
  39         130,
  40         154,
  41         183,
  42         218,
  43         259,
  44         308,
  45         367,
  46         436,
  47         518,
  48         616,
  49         733,
  50         872,
  51         1037,
  52         1233,
  53         1466,
  54         1744,
  55         2073,
  56         2466,
  57         2933,
  58         3487,
  59         4147,
  60         4932,
  61         5865,
  62         6975,
  63         8294,
  64         9864,
  65         11730,
  66         13949,
  67         16589,
  68         19728,
  69         23460,
  70         27899,
  71         33178,
  72         39455,
  73         46920,
  74         55798,
  75         66355,
  76         78910,
  77         93840,
  78         111596,
  79         132710,
  80         157820,
  81         187681,
  82         223191
  83 };
  84 
  85 #define to_wm8350_led(led_cdev) \
  86         container_of(led_cdev, struct wm8350_led, cdev)
  87 
  88 static int wm8350_led_enable(struct wm8350_led *led)
  89 {
  90         int ret = 0;
  91 
  92         if (led->enabled)
  93                 return ret;
  94 
  95         ret = regulator_enable(led->isink);
  96         if (ret != 0) {
  97                 dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
  98                 return ret;
  99         }
 100 
 101         ret = regulator_enable(led->dcdc);
 102         if (ret != 0) {
 103                 dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
 104                 regulator_disable(led->isink);
 105                 return ret;
 106         }
 107 
 108         led->enabled = 1;
 109 
 110         return ret;
 111 }
 112 
 113 static int wm8350_led_disable(struct wm8350_led *led)
 114 {
 115         int ret = 0;
 116 
 117         if (!led->enabled)
 118                 return ret;
 119 
 120         ret = regulator_disable(led->dcdc);
 121         if (ret != 0) {
 122                 dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
 123                 return ret;
 124         }
 125 
 126         ret = regulator_disable(led->isink);
 127         if (ret != 0) {
 128                 dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
 129                 ret = regulator_enable(led->dcdc);
 130                 if (ret != 0)
 131                         dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n",
 132                                 ret);
 133                 return ret;
 134         }
 135 
 136         led->enabled = 0;
 137 
 138         return ret;
 139 }
 140 
 141 static int wm8350_led_set(struct led_classdev *led_cdev,
 142                            enum led_brightness value)
 143 {
 144         struct wm8350_led *led = to_wm8350_led(led_cdev);
 145         unsigned long flags;
 146         int ret;
 147         int uA;
 148 
 149         led->value = value;
 150 
 151         spin_lock_irqsave(&led->value_lock, flags);
 152 
 153         if (led->value == LED_OFF) {
 154                 spin_unlock_irqrestore(&led->value_lock, flags);
 155                 return wm8350_led_disable(led);
 156         }
 157 
 158         /* This scales linearly into the index of valid current
 159          * settings which results in a linear scaling of perceived
 160          * brightness due to the non-linear current settings provided
 161          * by the hardware.
 162          */
 163         uA = (led->max_uA_index * led->value) / LED_FULL;
 164         spin_unlock_irqrestore(&led->value_lock, flags);
 165         BUG_ON(uA >= ARRAY_SIZE(isink_cur));
 166 
 167         ret = regulator_set_current_limit(led->isink, isink_cur[uA],
 168                                           isink_cur[uA]);
 169         if (ret != 0) {
 170                 dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
 171                         isink_cur[uA], ret);
 172                 return ret;
 173         }
 174 
 175         return wm8350_led_enable(led);
 176 }
 177 
 178 static void wm8350_led_shutdown(struct platform_device *pdev)
 179 {
 180         struct wm8350_led *led = platform_get_drvdata(pdev);
 181 
 182         led->value = LED_OFF;
 183         wm8350_led_disable(led);
 184 }
 185 
 186 static int wm8350_led_probe(struct platform_device *pdev)
 187 {
 188         struct regulator *isink, *dcdc;
 189         struct wm8350_led *led;
 190         struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
 191         int i;
 192 
 193         if (pdata == NULL) {
 194                 dev_err(&pdev->dev, "no platform data\n");
 195                 return -ENODEV;
 196         }
 197 
 198         if (pdata->max_uA < isink_cur[0]) {
 199                 dev_err(&pdev->dev, "Invalid maximum current %duA\n",
 200                         pdata->max_uA);
 201                 return -EINVAL;
 202         }
 203 
 204         isink = devm_regulator_get(&pdev->dev, "led_isink");
 205         if (IS_ERR(isink)) {
 206                 dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__);
 207                 return PTR_ERR(isink);
 208         }
 209 
 210         dcdc = devm_regulator_get(&pdev->dev, "led_vcc");
 211         if (IS_ERR(dcdc)) {
 212                 dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__);
 213                 return PTR_ERR(dcdc);
 214         }
 215 
 216         led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
 217         if (led == NULL)
 218                 return -ENOMEM;
 219 
 220         led->cdev.brightness_set_blocking = wm8350_led_set;
 221         led->cdev.default_trigger = pdata->default_trigger;
 222         led->cdev.name = pdata->name;
 223         led->cdev.flags |= LED_CORE_SUSPENDRESUME;
 224         led->enabled = regulator_is_enabled(isink);
 225         led->isink = isink;
 226         led->dcdc = dcdc;
 227 
 228         for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
 229                 if (isink_cur[i] >= pdata->max_uA)
 230                         break;
 231         led->max_uA_index = i;
 232         if (pdata->max_uA != isink_cur[i])
 233                 dev_warn(&pdev->dev,
 234                          "Maximum current %duA is not directly supported,"
 235                          " check platform data\n",
 236                          pdata->max_uA);
 237 
 238         spin_lock_init(&led->value_lock);
 239         led->value = LED_OFF;
 240         platform_set_drvdata(pdev, led);
 241 
 242         return led_classdev_register(&pdev->dev, &led->cdev);
 243 }
 244 
 245 static int wm8350_led_remove(struct platform_device *pdev)
 246 {
 247         struct wm8350_led *led = platform_get_drvdata(pdev);
 248 
 249         led_classdev_unregister(&led->cdev);
 250         wm8350_led_disable(led);
 251         return 0;
 252 }
 253 
 254 static struct platform_driver wm8350_led_driver = {
 255         .driver = {
 256                    .name = "wm8350-led",
 257                    },
 258         .probe = wm8350_led_probe,
 259         .remove = wm8350_led_remove,
 260         .shutdown = wm8350_led_shutdown,
 261 };
 262 
 263 module_platform_driver(wm8350_led_driver);
 264 
 265 MODULE_AUTHOR("Mark Brown");
 266 MODULE_DESCRIPTION("WM8350 LED driver");
 267 MODULE_LICENSE("GPL");
 268 MODULE_ALIAS("platform:wm8350-led");

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