linux/drivers/leds/leds-pm8058.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved.
   3 */
   4#include <linux/leds.h>
   5#include <linux/module.h>
   6#include <linux/of.h>
   7#include <linux/of_device.h>
   8#include <linux/platform_device.h>
   9#include <linux/pm.h>
  10#include <linux/regmap.h>
  11
  12#define PM8058_LED_TYPE_COMMON  0x00
  13#define PM8058_LED_TYPE_KEYPAD  0x01
  14#define PM8058_LED_TYPE_FLASH   0x02
  15
  16#define PM8058_LED_TYPE_COMMON_MASK     0xf8
  17#define PM8058_LED_TYPE_KEYPAD_MASK     0xf0
  18#define PM8058_LED_TYPE_COMMON_SHIFT    3
  19#define PM8058_LED_TYPE_KEYPAD_SHIFT    4
  20
  21struct pm8058_led {
  22        struct regmap *map;
  23        u32 reg;
  24        u32 ledtype;
  25        struct led_classdev cdev;
  26};
  27
  28static void pm8058_led_set(struct led_classdev *cled,
  29        enum led_brightness value)
  30{
  31        struct pm8058_led *led;
  32        int ret = 0;
  33        unsigned int mask = 0;
  34        unsigned int val = 0;
  35
  36        led = container_of(cled, struct pm8058_led, cdev);
  37        switch (led->ledtype) {
  38        case PM8058_LED_TYPE_COMMON:
  39                mask = PM8058_LED_TYPE_COMMON_MASK;
  40                val = value << PM8058_LED_TYPE_COMMON_SHIFT;
  41                break;
  42        case PM8058_LED_TYPE_KEYPAD:
  43        case PM8058_LED_TYPE_FLASH:
  44                mask = PM8058_LED_TYPE_KEYPAD_MASK;
  45                val = value << PM8058_LED_TYPE_KEYPAD_SHIFT;
  46                break;
  47        default:
  48                break;
  49        }
  50
  51        ret = regmap_update_bits(led->map, led->reg, mask, val);
  52        if (ret)
  53                pr_err("Failed to set LED brightness\n");
  54}
  55
  56static enum led_brightness pm8058_led_get(struct led_classdev *cled)
  57{
  58        struct pm8058_led *led;
  59        int ret;
  60        unsigned int val;
  61
  62        led = container_of(cled, struct pm8058_led, cdev);
  63
  64        ret = regmap_read(led->map, led->reg, &val);
  65        if (ret) {
  66                pr_err("Failed to get LED brightness\n");
  67                return LED_OFF;
  68        }
  69
  70        switch (led->ledtype) {
  71        case PM8058_LED_TYPE_COMMON:
  72                val &= PM8058_LED_TYPE_COMMON_MASK;
  73                val >>= PM8058_LED_TYPE_COMMON_SHIFT;
  74                break;
  75        case PM8058_LED_TYPE_KEYPAD:
  76        case PM8058_LED_TYPE_FLASH:
  77                val &= PM8058_LED_TYPE_KEYPAD_MASK;
  78                val >>= PM8058_LED_TYPE_KEYPAD_SHIFT;
  79                break;
  80        default:
  81                val = LED_OFF;
  82                break;
  83        }
  84
  85        return val;
  86}
  87
  88static int pm8058_led_probe(struct platform_device *pdev)
  89{
  90        struct pm8058_led *led;
  91        struct device_node *np = pdev->dev.of_node;
  92        int ret;
  93        struct regmap *map;
  94        const char *state;
  95        enum led_brightness maxbright;
  96
  97        led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
  98        if (!led)
  99                return -ENOMEM;
 100
 101        led->ledtype = (u32)(unsigned long)of_device_get_match_data(&pdev->dev);
 102
 103        map = dev_get_regmap(pdev->dev.parent, NULL);
 104        if (!map) {
 105                dev_err(&pdev->dev, "Parent regmap unavailable.\n");
 106                return -ENXIO;
 107        }
 108        led->map = map;
 109
 110        ret = of_property_read_u32(np, "reg", &led->reg);
 111        if (ret) {
 112                dev_err(&pdev->dev, "no register offset specified\n");
 113                return -EINVAL;
 114        }
 115
 116        /* Use label else node name */
 117        led->cdev.name = of_get_property(np, "label", NULL) ? : np->name;
 118        led->cdev.default_trigger =
 119                of_get_property(np, "linux,default-trigger", NULL);
 120        led->cdev.brightness_set = pm8058_led_set;
 121        led->cdev.brightness_get = pm8058_led_get;
 122        if (led->ledtype == PM8058_LED_TYPE_COMMON)
 123                maxbright = 31; /* 5 bits */
 124        else
 125                maxbright = 15; /* 4 bits */
 126        led->cdev.max_brightness = maxbright;
 127
 128        state = of_get_property(np, "default-state", NULL);
 129        if (state) {
 130                if (!strcmp(state, "keep")) {
 131                        led->cdev.brightness = pm8058_led_get(&led->cdev);
 132                } else if (!strcmp(state, "on")) {
 133                        led->cdev.brightness = maxbright;
 134                        pm8058_led_set(&led->cdev, maxbright);
 135                } else {
 136                        led->cdev.brightness = LED_OFF;
 137                        pm8058_led_set(&led->cdev, LED_OFF);
 138                }
 139        }
 140
 141        if (led->ledtype == PM8058_LED_TYPE_KEYPAD ||
 142            led->ledtype == PM8058_LED_TYPE_FLASH)
 143                led->cdev.flags = LED_CORE_SUSPENDRESUME;
 144
 145        ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
 146        if (ret) {
 147                dev_err(&pdev->dev, "unable to register led \"%s\"\n",
 148                        led->cdev.name);
 149                return ret;
 150        }
 151
 152        return 0;
 153}
 154
 155static const struct of_device_id pm8058_leds_id_table[] = {
 156        {
 157                .compatible = "qcom,pm8058-led",
 158                .data = (void *)PM8058_LED_TYPE_COMMON
 159        },
 160        {
 161                .compatible = "qcom,pm8058-keypad-led",
 162                .data = (void *)PM8058_LED_TYPE_KEYPAD
 163        },
 164        {
 165                .compatible = "qcom,pm8058-flash-led",
 166                .data = (void *)PM8058_LED_TYPE_FLASH
 167        },
 168        { },
 169};
 170MODULE_DEVICE_TABLE(of, pm8058_leds_id_table);
 171
 172static struct platform_driver pm8058_led_driver = {
 173        .probe          = pm8058_led_probe,
 174        .driver         = {
 175                .name   = "pm8058-leds",
 176                .of_match_table = pm8058_leds_id_table,
 177        },
 178};
 179module_platform_driver(pm8058_led_driver);
 180
 181MODULE_DESCRIPTION("PM8058 LEDs driver");
 182MODULE_LICENSE("GPL v2");
 183MODULE_ALIAS("platform:pm8058-leds");
 184