Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved.
0003  */
0004 #include <linux/leds.h>
0005 #include <linux/module.h>
0006 #include <linux/of.h>
0007 #include <linux/of_device.h>
0008 #include <linux/platform_device.h>
0009 #include <linux/pm.h>
0010 #include <linux/regmap.h>
0011 
0012 #define PM8058_LED_TYPE_COMMON  0x00
0013 #define PM8058_LED_TYPE_KEYPAD  0x01
0014 #define PM8058_LED_TYPE_FLASH   0x02
0015 
0016 #define PM8058_LED_TYPE_COMMON_MASK 0xf8
0017 #define PM8058_LED_TYPE_KEYPAD_MASK 0xf0
0018 #define PM8058_LED_TYPE_COMMON_SHIFT    3
0019 #define PM8058_LED_TYPE_KEYPAD_SHIFT    4
0020 
0021 struct pm8058_led {
0022     struct regmap *map;
0023     u32 reg;
0024     u32 ledtype;
0025     struct led_classdev cdev;
0026 };
0027 
0028 static void pm8058_led_set(struct led_classdev *cled,
0029     enum led_brightness value)
0030 {
0031     struct pm8058_led *led;
0032     int ret = 0;
0033     unsigned int mask = 0;
0034     unsigned int val = 0;
0035 
0036     led = container_of(cled, struct pm8058_led, cdev);
0037     switch (led->ledtype) {
0038     case PM8058_LED_TYPE_COMMON:
0039         mask = PM8058_LED_TYPE_COMMON_MASK;
0040         val = value << PM8058_LED_TYPE_COMMON_SHIFT;
0041         break;
0042     case PM8058_LED_TYPE_KEYPAD:
0043     case PM8058_LED_TYPE_FLASH:
0044         mask = PM8058_LED_TYPE_KEYPAD_MASK;
0045         val = value << PM8058_LED_TYPE_KEYPAD_SHIFT;
0046         break;
0047     default:
0048         break;
0049     }
0050 
0051     ret = regmap_update_bits(led->map, led->reg, mask, val);
0052     if (ret)
0053         pr_err("Failed to set LED brightness\n");
0054 }
0055 
0056 static enum led_brightness pm8058_led_get(struct led_classdev *cled)
0057 {
0058     struct pm8058_led *led;
0059     int ret;
0060     unsigned int val;
0061 
0062     led = container_of(cled, struct pm8058_led, cdev);
0063 
0064     ret = regmap_read(led->map, led->reg, &val);
0065     if (ret) {
0066         pr_err("Failed to get LED brightness\n");
0067         return LED_OFF;
0068     }
0069 
0070     switch (led->ledtype) {
0071     case PM8058_LED_TYPE_COMMON:
0072         val &= PM8058_LED_TYPE_COMMON_MASK;
0073         val >>= PM8058_LED_TYPE_COMMON_SHIFT;
0074         break;
0075     case PM8058_LED_TYPE_KEYPAD:
0076     case PM8058_LED_TYPE_FLASH:
0077         val &= PM8058_LED_TYPE_KEYPAD_MASK;
0078         val >>= PM8058_LED_TYPE_KEYPAD_SHIFT;
0079         break;
0080     default:
0081         val = LED_OFF;
0082         break;
0083     }
0084 
0085     return val;
0086 }
0087 
0088 static int pm8058_led_probe(struct platform_device *pdev)
0089 {
0090     struct led_init_data init_data = {};
0091     struct device *dev = &pdev->dev;
0092     struct pm8058_led *led;
0093     struct device_node *np;
0094     int ret;
0095     struct regmap *map;
0096     const char *state;
0097     enum led_brightness maxbright;
0098 
0099     led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
0100     if (!led)
0101         return -ENOMEM;
0102 
0103     led->ledtype = (u32)(unsigned long)of_device_get_match_data(dev);
0104 
0105     map = dev_get_regmap(dev->parent, NULL);
0106     if (!map) {
0107         dev_err(dev, "Parent regmap unavailable.\n");
0108         return -ENXIO;
0109     }
0110     led->map = map;
0111 
0112     np = dev_of_node(dev);
0113 
0114     ret = of_property_read_u32(np, "reg", &led->reg);
0115     if (ret) {
0116         dev_err(dev, "no register offset specified\n");
0117         return -EINVAL;
0118     }
0119 
0120     led->cdev.brightness_set = pm8058_led_set;
0121     led->cdev.brightness_get = pm8058_led_get;
0122     if (led->ledtype == PM8058_LED_TYPE_COMMON)
0123         maxbright = 31; /* 5 bits */
0124     else
0125         maxbright = 15; /* 4 bits */
0126     led->cdev.max_brightness = maxbright;
0127 
0128     state = of_get_property(np, "default-state", NULL);
0129     if (state) {
0130         if (!strcmp(state, "keep")) {
0131             led->cdev.brightness = pm8058_led_get(&led->cdev);
0132         } else if (!strcmp(state, "on")) {
0133             led->cdev.brightness = maxbright;
0134             pm8058_led_set(&led->cdev, maxbright);
0135         } else {
0136             led->cdev.brightness = LED_OFF;
0137             pm8058_led_set(&led->cdev, LED_OFF);
0138         }
0139     }
0140 
0141     if (led->ledtype == PM8058_LED_TYPE_KEYPAD ||
0142         led->ledtype == PM8058_LED_TYPE_FLASH)
0143         led->cdev.flags = LED_CORE_SUSPENDRESUME;
0144 
0145     init_data.fwnode = of_fwnode_handle(np);
0146 
0147     ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
0148     if (ret)
0149         dev_err(dev, "Failed to register LED for %pOF\n", np);
0150 
0151     return ret;
0152 }
0153 
0154 static const struct of_device_id pm8058_leds_id_table[] = {
0155     {
0156         .compatible = "qcom,pm8058-led",
0157         .data = (void *)PM8058_LED_TYPE_COMMON
0158     },
0159     {
0160         .compatible = "qcom,pm8058-keypad-led",
0161         .data = (void *)PM8058_LED_TYPE_KEYPAD
0162     },
0163     {
0164         .compatible = "qcom,pm8058-flash-led",
0165         .data = (void *)PM8058_LED_TYPE_FLASH
0166     },
0167     { },
0168 };
0169 MODULE_DEVICE_TABLE(of, pm8058_leds_id_table);
0170 
0171 static struct platform_driver pm8058_led_driver = {
0172     .probe      = pm8058_led_probe,
0173     .driver     = {
0174         .name   = "pm8058-leds",
0175         .of_match_table = pm8058_leds_id_table,
0176     },
0177 };
0178 module_platform_driver(pm8058_led_driver);
0179 
0180 MODULE_DESCRIPTION("PM8058 LEDs driver");
0181 MODULE_LICENSE("GPL v2");
0182 MODULE_ALIAS("platform:pm8058-leds");