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