Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

leds: add PM8058 LEDs driver

This adds a driver for the six PM8058 LEDs, three ordinary LEDs,
two "flash" LEDs and one "keypad" LED.

The "keypad" and "flash" LEDs are not really hard-wired to these
usecases: for example on the APQ8060 Dragonboard, the "keypad"
LED is instead used to drive an IR LED used for the proximity
sensor. The "flash" LEDs are just ordinary high-current LED
drivers.

Cc: linux-arm-msm@vger.kernel.org
Cc: Andy Gross <andy.gross@linaro.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>

authored by

Linus Walleij and committed by
Jacek Anaszewski
7f866986 9a6b1f60

+200
+8
drivers/leds/Kconfig
··· 643 643 This option enabled support for the LEDs on the ARM Versatile 644 644 and RealView boards. Say Y to enabled these. 645 645 646 + config LEDS_PM8058 647 + tristate "LED Support for the Qualcomm PM8058 PMIC" 648 + depends on MFD_PM8921_CORE 649 + depends on LEDS_CLASS 650 + help 651 + Choose this option if you want to use the LED drivers in 652 + the Qualcomm PM8058 PMIC. 653 + 646 654 comment "LED Triggers" 647 655 source "drivers/leds/trigger/Kconfig" 648 656
+1
drivers/leds/Makefile
··· 69 69 obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o 70 70 obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o 71 71 obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o 72 + obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o 72 73 73 74 # LED SPI Drivers 74 75 obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
+191
drivers/leds/leds-pm8058.c
··· 1 + /* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved. 2 + * 3 + * This program is free software; you can redistribute it and/or modify 4 + * it under the terms of the GNU General Public License version 2 and 5 + * only version 2 as published by the Free Software Foundation. 6 + * 7 + * This program is distributed in the hope that it will be useful, 8 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 + * GNU General Public License for more details. 11 + */ 12 + #include <linux/leds.h> 13 + #include <linux/module.h> 14 + #include <linux/of.h> 15 + #include <linux/of_device.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/pm.h> 18 + #include <linux/regmap.h> 19 + 20 + #define PM8058_LED_TYPE_COMMON 0x00 21 + #define PM8058_LED_TYPE_KEYPAD 0x01 22 + #define PM8058_LED_TYPE_FLASH 0x02 23 + 24 + #define PM8058_LED_TYPE_COMMON_MASK 0xf8 25 + #define PM8058_LED_TYPE_KEYPAD_MASK 0xf0 26 + #define PM8058_LED_TYPE_COMMON_SHIFT 3 27 + #define PM8058_LED_TYPE_KEYPAD_SHIFT 4 28 + 29 + struct pm8058_led { 30 + struct regmap *map; 31 + u32 reg; 32 + u32 ledtype; 33 + struct led_classdev cdev; 34 + }; 35 + 36 + static void pm8058_led_set(struct led_classdev *cled, 37 + enum led_brightness value) 38 + { 39 + struct pm8058_led *led; 40 + int ret = 0; 41 + unsigned int mask = 0; 42 + unsigned int val = 0; 43 + 44 + led = container_of(cled, struct pm8058_led, cdev); 45 + switch (led->ledtype) { 46 + case PM8058_LED_TYPE_COMMON: 47 + mask = PM8058_LED_TYPE_COMMON_MASK; 48 + val = value << PM8058_LED_TYPE_COMMON_SHIFT; 49 + break; 50 + case PM8058_LED_TYPE_KEYPAD: 51 + case PM8058_LED_TYPE_FLASH: 52 + mask = PM8058_LED_TYPE_KEYPAD_MASK; 53 + val = value << PM8058_LED_TYPE_KEYPAD_SHIFT; 54 + break; 55 + default: 56 + break; 57 + } 58 + 59 + ret = regmap_update_bits(led->map, led->reg, mask, val); 60 + if (ret) 61 + pr_err("Failed to set LED brightness\n"); 62 + } 63 + 64 + static enum led_brightness pm8058_led_get(struct led_classdev *cled) 65 + { 66 + struct pm8058_led *led; 67 + int ret; 68 + unsigned int val; 69 + 70 + led = container_of(cled, struct pm8058_led, cdev); 71 + 72 + ret = regmap_read(led->map, led->reg, &val); 73 + if (ret) { 74 + pr_err("Failed to get LED brightness\n"); 75 + return LED_OFF; 76 + } 77 + 78 + switch (led->ledtype) { 79 + case PM8058_LED_TYPE_COMMON: 80 + val &= PM8058_LED_TYPE_COMMON_MASK; 81 + val >>= PM8058_LED_TYPE_COMMON_SHIFT; 82 + break; 83 + case PM8058_LED_TYPE_KEYPAD: 84 + case PM8058_LED_TYPE_FLASH: 85 + val &= PM8058_LED_TYPE_KEYPAD_MASK; 86 + val >>= PM8058_LED_TYPE_KEYPAD_SHIFT; 87 + break; 88 + default: 89 + val = LED_OFF; 90 + break; 91 + } 92 + 93 + return val; 94 + } 95 + 96 + static int pm8058_led_probe(struct platform_device *pdev) 97 + { 98 + struct pm8058_led *led; 99 + struct device_node *np = pdev->dev.of_node; 100 + int ret; 101 + struct regmap *map; 102 + const char *state; 103 + enum led_brightness maxbright; 104 + 105 + led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); 106 + if (!led) 107 + return -ENOMEM; 108 + 109 + led->ledtype = (u32)of_device_get_match_data(&pdev->dev); 110 + 111 + map = dev_get_regmap(pdev->dev.parent, NULL); 112 + if (!map) { 113 + dev_err(&pdev->dev, "Parent regmap unavailable.\n"); 114 + return -ENXIO; 115 + } 116 + led->map = map; 117 + 118 + ret = of_property_read_u32(np, "reg", &led->reg); 119 + if (ret) { 120 + dev_err(&pdev->dev, "no register offset specified\n"); 121 + return -EINVAL; 122 + } 123 + 124 + /* Use label else node name */ 125 + led->cdev.name = of_get_property(np, "label", NULL) ? : np->name; 126 + led->cdev.default_trigger = 127 + of_get_property(np, "linux,default-trigger", NULL); 128 + led->cdev.brightness_set = pm8058_led_set; 129 + led->cdev.brightness_get = pm8058_led_get; 130 + if (led->ledtype == PM8058_LED_TYPE_COMMON) 131 + maxbright = 31; /* 5 bits */ 132 + else 133 + maxbright = 15; /* 4 bits */ 134 + led->cdev.max_brightness = maxbright; 135 + 136 + state = of_get_property(np, "default-state", NULL); 137 + if (state) { 138 + if (!strcmp(state, "keep")) { 139 + led->cdev.brightness = pm8058_led_get(&led->cdev); 140 + } else if (!strcmp(state, "on")) { 141 + led->cdev.brightness = maxbright; 142 + pm8058_led_set(&led->cdev, maxbright); 143 + } else { 144 + led->cdev.brightness = LED_OFF; 145 + pm8058_led_set(&led->cdev, LED_OFF); 146 + } 147 + } 148 + 149 + if (led->ledtype == PM8058_LED_TYPE_KEYPAD || 150 + led->ledtype == PM8058_LED_TYPE_FLASH) 151 + led->cdev.flags = LED_CORE_SUSPENDRESUME; 152 + 153 + ret = devm_led_classdev_register(&pdev->dev, &led->cdev); 154 + if (ret) { 155 + dev_err(&pdev->dev, "unable to register led \"%s\"\n", 156 + led->cdev.name); 157 + return ret; 158 + } 159 + 160 + return 0; 161 + } 162 + 163 + static const struct of_device_id pm8058_leds_id_table[] = { 164 + { 165 + .compatible = "qcom,pm8058-led", 166 + .data = (void *)PM8058_LED_TYPE_COMMON 167 + }, 168 + { 169 + .compatible = "qcom,pm8058-keypad-led", 170 + .data = (void *)PM8058_LED_TYPE_KEYPAD 171 + }, 172 + { 173 + .compatible = "qcom,pm8058-flash-led", 174 + .data = (void *)PM8058_LED_TYPE_FLASH 175 + }, 176 + { }, 177 + }; 178 + MODULE_DEVICE_TABLE(of, pm8058_leds_id_table); 179 + 180 + static struct platform_driver pm8058_led_driver = { 181 + .probe = pm8058_led_probe, 182 + .driver = { 183 + .name = "pm8058-leds", 184 + .of_match_table = pm8058_leds_id_table, 185 + }, 186 + }; 187 + module_platform_driver(pm8058_led_driver); 188 + 189 + MODULE_DESCRIPTION("PM8058 LEDs driver"); 190 + MODULE_LICENSE("GPL v2"); 191 + MODULE_ALIAS("platform:pm8058-leds");