0001
0002
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;
0124 else
0125 maxbright = 15;
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");