diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2016-08-16 05:25:10 -0400 |
---|---|---|
committer | Jacek Anaszewski <j.anaszewski@samsung.com> | 2016-08-16 16:37:26 -0400 |
commit | 7f866986e7052197396d8a663fa009209af18aee (patch) | |
tree | 79bf1abd62cc82aaacd3843abf11a0d5ac60afb8 | |
parent | 9a6b1f608acd0948acbfc7ddafaf27bc00293302 (diff) |
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>
-rw-r--r-- | drivers/leds/Kconfig | 8 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-pm8058.c | 191 |
3 files changed, 200 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 6df9a2f098f5..969c720dfd10 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -643,6 +643,14 @@ config LEDS_VERSATILE | |||
643 | This option enabled support for the LEDs on the ARM Versatile | 643 | This option enabled support for the LEDs on the ARM Versatile |
644 | and RealView boards. Say Y to enabled these. | 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 | comment "LED Triggers" | 654 | comment "LED Triggers" |
647 | source "drivers/leds/trigger/Kconfig" | 655 | source "drivers/leds/trigger/Kconfig" |
648 | 656 | ||
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e7a0e4edadd4..06a27ef17554 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -69,6 +69,7 @@ obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o | |||
69 | obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o | 69 | obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o |
70 | obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o | 70 | obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o |
71 | obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o | 71 | obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o |
72 | obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o | ||
72 | 73 | ||
73 | # LED SPI Drivers | 74 | # LED SPI Drivers |
74 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 75 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o |
diff --git a/drivers/leds/leds-pm8058.c b/drivers/leds/leds-pm8058.c new file mode 100644 index 000000000000..a52674327857 --- /dev/null +++ b/drivers/leds/leds-pm8058.c | |||
@@ -0,0 +1,191 @@ | |||
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"); | ||