diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-15 21:28:25 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-15 21:28:25 -0500 |
commit | 2dbfca5a181973558277b28b1f4c36362291f5e0 (patch) | |
tree | e9d217adf53e3532939e7a3c3bcec94afd671306 /drivers/leds | |
parent | dab363f938a53ddaee60bfecc1aebdbb3d3af5f0 (diff) | |
parent | 2969bb18f8895eb4e0fbbc98efe706f15a3acff5 (diff) |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds
Pull LED subsystem update from Bryan Wu:
"We got some cleanup and driver for LP8860 as well as some patches for
LED Flash Class"
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds:
leds: lp8860: Fix module dependency
leds: lp8860: Introduce TI lp8860 4 channel LED driver
leds: Add support for setting brightness in a synchronous way
leds: implement sysfs interface locking mechanism
leds: syscon: handle multiple syscon instances
leds: delete copy/paste mistake
leds: regulator: Convert to devm_regulator_get_exclusive
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 11 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 29 | ||||
-rw-r--r-- | drivers/leds/led-core.c | 36 | ||||
-rw-r--r-- | drivers/leds/led-triggers.c | 16 | ||||
-rw-r--r-- | drivers/leds/leds-lp8860.c | 491 | ||||
-rw-r--r-- | drivers/leds/leds-regulator.c | 18 | ||||
-rw-r--r-- | drivers/leds/leds-syscon.c | 71 | ||||
-rw-r--r-- | drivers/leds/leds.h | 20 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-backlight.c | 8 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-default-on.c | 2 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-gpio.c | 6 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-heartbeat.c | 2 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-oneshot.c | 4 | ||||
-rw-r--r-- | drivers/leds/trigger/ledtrig-transient.c | 10 |
15 files changed, 645 insertions, 80 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index a210338cfeb1..a6c3d2f153f3 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -250,6 +250,17 @@ config LEDS_LP8788 | |||
250 | help | 250 | help |
251 | This option enables support for the Keyboard LEDs on the LP8788 PMIC. | 251 | This option enables support for the Keyboard LEDs on the LP8788 PMIC. |
252 | 252 | ||
253 | config LEDS_LP8860 | ||
254 | tristate "LED support for the TI LP8860 4 channel LED driver" | ||
255 | depends on LEDS_CLASS && I2C | ||
256 | select REGMAP_I2C | ||
257 | help | ||
258 | If you say yes here you get support for the TI LP8860 4 channel | ||
259 | LED driver. | ||
260 | This option enables support for the display cluster LEDs | ||
261 | on the LP8860 4 channel LED driver using the I2C communication | ||
262 | bus. | ||
263 | |||
253 | config LEDS_CLEVO_MAIL | 264 | config LEDS_CLEVO_MAIL |
254 | tristate "Mail LED on Clevo notebook" | 265 | tristate "Mail LED on Clevo notebook" |
255 | depends on LEDS_CLASS | 266 | depends on LEDS_CLASS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index a2b164741465..1c65a191d907 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -28,6 +28,7 @@ obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o | |||
28 | obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o | 28 | obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o |
29 | obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o | 29 | obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o |
30 | obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o | 30 | obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o |
31 | obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o | ||
31 | obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o | 32 | obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o |
32 | obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o | 33 | obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o |
33 | obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o | 34 | obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o |
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 7440c58b8e6f..dbeebac38d31 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c | |||
@@ -40,17 +40,27 @@ static ssize_t brightness_store(struct device *dev, | |||
40 | { | 40 | { |
41 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 41 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
42 | unsigned long state; | 42 | unsigned long state; |
43 | ssize_t ret = -EINVAL; | 43 | ssize_t ret; |
44 | |||
45 | mutex_lock(&led_cdev->led_access); | ||
46 | |||
47 | if (led_sysfs_is_disabled(led_cdev)) { | ||
48 | ret = -EBUSY; | ||
49 | goto unlock; | ||
50 | } | ||
44 | 51 | ||
45 | ret = kstrtoul(buf, 10, &state); | 52 | ret = kstrtoul(buf, 10, &state); |
46 | if (ret) | 53 | if (ret) |
47 | return ret; | 54 | goto unlock; |
48 | 55 | ||
49 | if (state == LED_OFF) | 56 | if (state == LED_OFF) |
50 | led_trigger_remove(led_cdev); | 57 | led_trigger_remove(led_cdev); |
51 | __led_set_brightness(led_cdev, state); | 58 | led_set_brightness(led_cdev, state); |
52 | 59 | ||
53 | return size; | 60 | ret = size; |
61 | unlock: | ||
62 | mutex_unlock(&led_cdev->led_access); | ||
63 | return ret; | ||
54 | } | 64 | } |
55 | static DEVICE_ATTR_RW(brightness); | 65 | static DEVICE_ATTR_RW(brightness); |
56 | 66 | ||
@@ -99,7 +109,7 @@ static void led_timer_function(unsigned long data) | |||
99 | unsigned long delay; | 109 | unsigned long delay; |
100 | 110 | ||
101 | if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { | 111 | if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { |
102 | __led_set_brightness(led_cdev, LED_OFF); | 112 | led_set_brightness_async(led_cdev, LED_OFF); |
103 | return; | 113 | return; |
104 | } | 114 | } |
105 | 115 | ||
@@ -122,7 +132,7 @@ static void led_timer_function(unsigned long data) | |||
122 | delay = led_cdev->blink_delay_off; | 132 | delay = led_cdev->blink_delay_off; |
123 | } | 133 | } |
124 | 134 | ||
125 | __led_set_brightness(led_cdev, brightness); | 135 | led_set_brightness_async(led_cdev, brightness); |
126 | 136 | ||
127 | /* Return in next iteration if led is in one-shot mode and we are in | 137 | /* Return in next iteration if led is in one-shot mode and we are in |
128 | * the final blink state so that the led is toggled each delay_on + | 138 | * the final blink state so that the led is toggled each delay_on + |
@@ -148,7 +158,7 @@ static void set_brightness_delayed(struct work_struct *ws) | |||
148 | 158 | ||
149 | led_stop_software_blink(led_cdev); | 159 | led_stop_software_blink(led_cdev); |
150 | 160 | ||
151 | __led_set_brightness(led_cdev, led_cdev->delayed_set_value); | 161 | led_set_brightness_async(led_cdev, led_cdev->delayed_set_value); |
152 | } | 162 | } |
153 | 163 | ||
154 | /** | 164 | /** |
@@ -214,6 +224,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | |||
214 | #ifdef CONFIG_LEDS_TRIGGERS | 224 | #ifdef CONFIG_LEDS_TRIGGERS |
215 | init_rwsem(&led_cdev->trigger_lock); | 225 | init_rwsem(&led_cdev->trigger_lock); |
216 | #endif | 226 | #endif |
227 | mutex_init(&led_cdev->led_access); | ||
217 | /* add to the list of leds */ | 228 | /* add to the list of leds */ |
218 | down_write(&leds_list_lock); | 229 | down_write(&leds_list_lock); |
219 | list_add_tail(&led_cdev->node, &leds_list); | 230 | list_add_tail(&led_cdev->node, &leds_list); |
@@ -222,6 +233,8 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | |||
222 | if (!led_cdev->max_brightness) | 233 | if (!led_cdev->max_brightness) |
223 | led_cdev->max_brightness = LED_FULL; | 234 | led_cdev->max_brightness = LED_FULL; |
224 | 235 | ||
236 | led_cdev->flags |= SET_BRIGHTNESS_ASYNC; | ||
237 | |||
225 | led_update_brightness(led_cdev); | 238 | led_update_brightness(led_cdev); |
226 | 239 | ||
227 | INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); | 240 | INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); |
@@ -267,6 +280,8 @@ void led_classdev_unregister(struct led_classdev *led_cdev) | |||
267 | down_write(&leds_list_lock); | 280 | down_write(&leds_list_lock); |
268 | list_del(&led_cdev->node); | 281 | list_del(&led_cdev->node); |
269 | up_write(&leds_list_lock); | 282 | up_write(&leds_list_lock); |
283 | |||
284 | mutex_destroy(&led_cdev->led_access); | ||
270 | } | 285 | } |
271 | EXPORT_SYMBOL_GPL(led_classdev_unregister); | 286 | EXPORT_SYMBOL_GPL(led_classdev_unregister); |
272 | 287 | ||
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index aaa8eba9099f..9886dace5ad2 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c | |||
@@ -42,13 +42,13 @@ static void led_set_software_blink(struct led_classdev *led_cdev, | |||
42 | 42 | ||
43 | /* never on - just set to off */ | 43 | /* never on - just set to off */ |
44 | if (!delay_on) { | 44 | if (!delay_on) { |
45 | __led_set_brightness(led_cdev, LED_OFF); | 45 | led_set_brightness_async(led_cdev, LED_OFF); |
46 | return; | 46 | return; |
47 | } | 47 | } |
48 | 48 | ||
49 | /* never off - just set to brightness */ | 49 | /* never off - just set to brightness */ |
50 | if (!delay_off) { | 50 | if (!delay_off) { |
51 | __led_set_brightness(led_cdev, led_cdev->blink_brightness); | 51 | led_set_brightness_async(led_cdev, led_cdev->blink_brightness); |
52 | return; | 52 | return; |
53 | } | 53 | } |
54 | 54 | ||
@@ -117,6 +117,8 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink); | |||
117 | void led_set_brightness(struct led_classdev *led_cdev, | 117 | void led_set_brightness(struct led_classdev *led_cdev, |
118 | enum led_brightness brightness) | 118 | enum led_brightness brightness) |
119 | { | 119 | { |
120 | int ret = 0; | ||
121 | |||
120 | /* delay brightness setting if need to stop soft-blink timer */ | 122 | /* delay brightness setting if need to stop soft-blink timer */ |
121 | if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { | 123 | if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { |
122 | led_cdev->delayed_set_value = brightness; | 124 | led_cdev->delayed_set_value = brightness; |
@@ -124,7 +126,17 @@ void led_set_brightness(struct led_classdev *led_cdev, | |||
124 | return; | 126 | return; |
125 | } | 127 | } |
126 | 128 | ||
127 | __led_set_brightness(led_cdev, brightness); | 129 | if (led_cdev->flags & SET_BRIGHTNESS_ASYNC) { |
130 | led_set_brightness_async(led_cdev, brightness); | ||
131 | return; | ||
132 | } else if (led_cdev->flags & SET_BRIGHTNESS_SYNC) | ||
133 | ret = led_set_brightness_sync(led_cdev, brightness); | ||
134 | else | ||
135 | ret = -EINVAL; | ||
136 | |||
137 | if (ret < 0) | ||
138 | dev_dbg(led_cdev->dev, "Setting LED brightness failed (%d)\n", | ||
139 | ret); | ||
128 | } | 140 | } |
129 | EXPORT_SYMBOL(led_set_brightness); | 141 | EXPORT_SYMBOL(led_set_brightness); |
130 | 142 | ||
@@ -143,3 +155,21 @@ int led_update_brightness(struct led_classdev *led_cdev) | |||
143 | return ret; | 155 | return ret; |
144 | } | 156 | } |
145 | EXPORT_SYMBOL(led_update_brightness); | 157 | EXPORT_SYMBOL(led_update_brightness); |
158 | |||
159 | /* Caller must ensure led_cdev->led_access held */ | ||
160 | void led_sysfs_disable(struct led_classdev *led_cdev) | ||
161 | { | ||
162 | lockdep_assert_held(&led_cdev->led_access); | ||
163 | |||
164 | led_cdev->flags |= LED_SYSFS_DISABLE; | ||
165 | } | ||
166 | EXPORT_SYMBOL_GPL(led_sysfs_disable); | ||
167 | |||
168 | /* Caller must ensure led_cdev->led_access held */ | ||
169 | void led_sysfs_enable(struct led_classdev *led_cdev) | ||
170 | { | ||
171 | lockdep_assert_held(&led_cdev->led_access); | ||
172 | |||
173 | led_cdev->flags &= ~LED_SYSFS_DISABLE; | ||
174 | } | ||
175 | EXPORT_SYMBOL_GPL(led_sysfs_enable); | ||
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index c3734f10fdd5..e8b1120f486d 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c | |||
@@ -37,6 +37,14 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, | |||
37 | char trigger_name[TRIG_NAME_MAX]; | 37 | char trigger_name[TRIG_NAME_MAX]; |
38 | struct led_trigger *trig; | 38 | struct led_trigger *trig; |
39 | size_t len; | 39 | size_t len; |
40 | int ret = count; | ||
41 | |||
42 | mutex_lock(&led_cdev->led_access); | ||
43 | |||
44 | if (led_sysfs_is_disabled(led_cdev)) { | ||
45 | ret = -EBUSY; | ||
46 | goto unlock; | ||
47 | } | ||
40 | 48 | ||
41 | trigger_name[sizeof(trigger_name) - 1] = '\0'; | 49 | trigger_name[sizeof(trigger_name) - 1] = '\0'; |
42 | strncpy(trigger_name, buf, sizeof(trigger_name) - 1); | 50 | strncpy(trigger_name, buf, sizeof(trigger_name) - 1); |
@@ -47,7 +55,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, | |||
47 | 55 | ||
48 | if (!strcmp(trigger_name, "none")) { | 56 | if (!strcmp(trigger_name, "none")) { |
49 | led_trigger_remove(led_cdev); | 57 | led_trigger_remove(led_cdev); |
50 | return count; | 58 | goto unlock; |
51 | } | 59 | } |
52 | 60 | ||
53 | down_read(&triggers_list_lock); | 61 | down_read(&triggers_list_lock); |
@@ -58,12 +66,14 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, | |||
58 | up_write(&led_cdev->trigger_lock); | 66 | up_write(&led_cdev->trigger_lock); |
59 | 67 | ||
60 | up_read(&triggers_list_lock); | 68 | up_read(&triggers_list_lock); |
61 | return count; | 69 | goto unlock; |
62 | } | 70 | } |
63 | } | 71 | } |
64 | up_read(&triggers_list_lock); | 72 | up_read(&triggers_list_lock); |
65 | 73 | ||
66 | return -EINVAL; | 74 | unlock: |
75 | mutex_unlock(&led_cdev->led_access); | ||
76 | return ret; | ||
67 | } | 77 | } |
68 | EXPORT_SYMBOL_GPL(led_trigger_store); | 78 | EXPORT_SYMBOL_GPL(led_trigger_store); |
69 | 79 | ||
diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c new file mode 100644 index 000000000000..840e93f3ab3e --- /dev/null +++ b/drivers/leds/leds-lp8860.c | |||
@@ -0,0 +1,491 @@ | |||
1 | /* | ||
2 | * TI LP8860 4-Channel LED Driver | ||
3 | * | ||
4 | * Copyright (C) 2014 Texas Instruments | ||
5 | * | ||
6 | * Author: Dan Murphy <dmurphy@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/leds.h> | ||
17 | #include <linux/regmap.h> | ||
18 | #include <linux/regulator/consumer.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/mutex.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/of_gpio.h> | ||
23 | #include <linux/gpio/consumer.h> | ||
24 | #include <linux/slab.h> | ||
25 | |||
26 | #define LP8860_DISP_CL1_BRT_MSB 0x00 | ||
27 | #define LP8860_DISP_CL1_BRT_LSB 0x01 | ||
28 | #define LP8860_DISP_CL1_CURR_MSB 0x02 | ||
29 | #define LP8860_DISP_CL1_CURR_LSB 0x03 | ||
30 | #define LP8860_CL2_BRT_MSB 0x04 | ||
31 | #define LP8860_CL2_BRT_LSB 0x05 | ||
32 | #define LP8860_CL2_CURRENT 0x06 | ||
33 | #define LP8860_CL3_BRT_MSB 0x07 | ||
34 | #define LP8860_CL3_BRT_LSB 0x08 | ||
35 | #define LP8860_CL3_CURRENT 0x09 | ||
36 | #define LP8860_CL4_BRT_MSB 0x0a | ||
37 | #define LP8860_CL4_BRT_LSB 0x0b | ||
38 | #define LP8860_CL4_CURRENT 0x0c | ||
39 | #define LP8860_CONFIG 0x0d | ||
40 | #define LP8860_STATUS 0x0e | ||
41 | #define LP8860_FAULT 0x0f | ||
42 | #define LP8860_LED_FAULT 0x10 | ||
43 | #define LP8860_FAULT_CLEAR 0x11 | ||
44 | #define LP8860_ID 0x12 | ||
45 | #define LP8860_TEMP_MSB 0x13 | ||
46 | #define LP8860_TEMP_LSB 0x14 | ||
47 | #define LP8860_DISP_LED_CURR_MSB 0x15 | ||
48 | #define LP8860_DISP_LED_CURR_LSB 0x16 | ||
49 | #define LP8860_DISP_LED_PWM_MSB 0x17 | ||
50 | #define LP8860_DISP_LED_PWM_LSB 0x18 | ||
51 | #define LP8860_EEPROM_CNTRL 0x19 | ||
52 | #define LP8860_EEPROM_UNLOCK 0x1a | ||
53 | |||
54 | #define LP8860_EEPROM_REG_0 0x60 | ||
55 | #define LP8860_EEPROM_REG_1 0x61 | ||
56 | #define LP8860_EEPROM_REG_2 0x62 | ||
57 | #define LP8860_EEPROM_REG_3 0x63 | ||
58 | #define LP8860_EEPROM_REG_4 0x64 | ||
59 | #define LP8860_EEPROM_REG_5 0x65 | ||
60 | #define LP8860_EEPROM_REG_6 0x66 | ||
61 | #define LP8860_EEPROM_REG_7 0x67 | ||
62 | #define LP8860_EEPROM_REG_8 0x68 | ||
63 | #define LP8860_EEPROM_REG_9 0x69 | ||
64 | #define LP8860_EEPROM_REG_10 0x6a | ||
65 | #define LP8860_EEPROM_REG_11 0x6b | ||
66 | #define LP8860_EEPROM_REG_12 0x6c | ||
67 | #define LP8860_EEPROM_REG_13 0x6d | ||
68 | #define LP8860_EEPROM_REG_14 0x6e | ||
69 | #define LP8860_EEPROM_REG_15 0x6f | ||
70 | #define LP8860_EEPROM_REG_16 0x70 | ||
71 | #define LP8860_EEPROM_REG_17 0x71 | ||
72 | #define LP8860_EEPROM_REG_18 0x72 | ||
73 | #define LP8860_EEPROM_REG_19 0x73 | ||
74 | #define LP8860_EEPROM_REG_20 0x74 | ||
75 | #define LP8860_EEPROM_REG_21 0x75 | ||
76 | #define LP8860_EEPROM_REG_22 0x76 | ||
77 | #define LP8860_EEPROM_REG_23 0x77 | ||
78 | #define LP8860_EEPROM_REG_24 0x78 | ||
79 | |||
80 | #define LP8860_LOCK_EEPROM 0x00 | ||
81 | #define LP8860_UNLOCK_EEPROM 0x01 | ||
82 | #define LP8860_PROGRAM_EEPROM 0x02 | ||
83 | #define LP8860_EEPROM_CODE_1 0x08 | ||
84 | #define LP8860_EEPROM_CODE_2 0xba | ||
85 | #define LP8860_EEPROM_CODE_3 0xef | ||
86 | |||
87 | #define LP8860_CLEAR_FAULTS 0x01 | ||
88 | |||
89 | #define LP8860_DISP_LED_NAME "display_cluster" | ||
90 | |||
91 | /** | ||
92 | * struct lp8860_led - | ||
93 | * @lock - Lock for reading/writing the device | ||
94 | * @work - Work item used to off load the brightness register writes | ||
95 | * @client - Pointer to the I2C client | ||
96 | * @led_dev - led class device pointer | ||
97 | * @regmap - Devices register map | ||
98 | * @eeprom_regmap - EEPROM register map | ||
99 | * @enable_gpio - VDDIO/EN gpio to enable communication interface | ||
100 | * @regulator - LED supply regulator pointer | ||
101 | * @brightness - Current brightness value requested | ||
102 | * @label - LED label | ||
103 | **/ | ||
104 | struct lp8860_led { | ||
105 | struct mutex lock; | ||
106 | struct work_struct work; | ||
107 | struct i2c_client *client; | ||
108 | struct led_classdev led_dev; | ||
109 | struct regmap *regmap; | ||
110 | struct regmap *eeprom_regmap; | ||
111 | struct gpio_desc *enable_gpio; | ||
112 | struct regulator *regulator; | ||
113 | enum led_brightness brightness; | ||
114 | const char *label; | ||
115 | }; | ||
116 | |||
117 | struct lp8860_eeprom_reg { | ||
118 | uint8_t reg; | ||
119 | uint8_t value; | ||
120 | }; | ||
121 | |||
122 | static struct lp8860_eeprom_reg lp8860_eeprom_disp_regs[] = { | ||
123 | { LP8860_EEPROM_REG_0, 0xed }, | ||
124 | { LP8860_EEPROM_REG_1, 0xdf }, | ||
125 | { LP8860_EEPROM_REG_2, 0xdc }, | ||
126 | { LP8860_EEPROM_REG_3, 0xf0 }, | ||
127 | { LP8860_EEPROM_REG_4, 0xdf }, | ||
128 | { LP8860_EEPROM_REG_5, 0xe5 }, | ||
129 | { LP8860_EEPROM_REG_6, 0xf2 }, | ||
130 | { LP8860_EEPROM_REG_7, 0x77 }, | ||
131 | { LP8860_EEPROM_REG_8, 0x77 }, | ||
132 | { LP8860_EEPROM_REG_9, 0x71 }, | ||
133 | { LP8860_EEPROM_REG_10, 0x3f }, | ||
134 | { LP8860_EEPROM_REG_11, 0xb7 }, | ||
135 | { LP8860_EEPROM_REG_12, 0x17 }, | ||
136 | { LP8860_EEPROM_REG_13, 0xef }, | ||
137 | { LP8860_EEPROM_REG_14, 0xb0 }, | ||
138 | { LP8860_EEPROM_REG_15, 0x87 }, | ||
139 | { LP8860_EEPROM_REG_16, 0xce }, | ||
140 | { LP8860_EEPROM_REG_17, 0x72 }, | ||
141 | { LP8860_EEPROM_REG_18, 0xe5 }, | ||
142 | { LP8860_EEPROM_REG_19, 0xdf }, | ||
143 | { LP8860_EEPROM_REG_20, 0x35 }, | ||
144 | { LP8860_EEPROM_REG_21, 0x06 }, | ||
145 | { LP8860_EEPROM_REG_22, 0xdc }, | ||
146 | { LP8860_EEPROM_REG_23, 0x88 }, | ||
147 | { LP8860_EEPROM_REG_24, 0x3E }, | ||
148 | }; | ||
149 | |||
150 | static int lp8860_unlock_eeprom(struct lp8860_led *led, int lock) | ||
151 | { | ||
152 | int ret; | ||
153 | |||
154 | mutex_lock(&led->lock); | ||
155 | |||
156 | if (lock == LP8860_UNLOCK_EEPROM) { | ||
157 | ret = regmap_write(led->regmap, | ||
158 | LP8860_EEPROM_UNLOCK, | ||
159 | LP8860_EEPROM_CODE_1); | ||
160 | if (ret) { | ||
161 | dev_err(&led->client->dev, "EEPROM Unlock failed\n"); | ||
162 | goto out; | ||
163 | } | ||
164 | |||
165 | ret = regmap_write(led->regmap, | ||
166 | LP8860_EEPROM_UNLOCK, | ||
167 | LP8860_EEPROM_CODE_2); | ||
168 | if (ret) { | ||
169 | dev_err(&led->client->dev, "EEPROM Unlock failed\n"); | ||
170 | goto out; | ||
171 | } | ||
172 | ret = regmap_write(led->regmap, | ||
173 | LP8860_EEPROM_UNLOCK, | ||
174 | LP8860_EEPROM_CODE_3); | ||
175 | if (ret) { | ||
176 | dev_err(&led->client->dev, "EEPROM Unlock failed\n"); | ||
177 | goto out; | ||
178 | } | ||
179 | } else { | ||
180 | ret = regmap_write(led->regmap, | ||
181 | LP8860_EEPROM_UNLOCK, | ||
182 | LP8860_LOCK_EEPROM); | ||
183 | } | ||
184 | |||
185 | out: | ||
186 | mutex_unlock(&led->lock); | ||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | static int lp8860_fault_check(struct lp8860_led *led) | ||
191 | { | ||
192 | int ret, fault; | ||
193 | unsigned int read_buf; | ||
194 | |||
195 | ret = regmap_read(led->regmap, LP8860_LED_FAULT, &read_buf); | ||
196 | if (ret) | ||
197 | goto out; | ||
198 | |||
199 | fault = read_buf; | ||
200 | |||
201 | ret = regmap_read(led->regmap, LP8860_FAULT, &read_buf); | ||
202 | if (ret) | ||
203 | goto out; | ||
204 | |||
205 | fault |= read_buf; | ||
206 | |||
207 | /* Attempt to clear any faults */ | ||
208 | if (fault) | ||
209 | ret = regmap_write(led->regmap, LP8860_FAULT_CLEAR, | ||
210 | LP8860_CLEAR_FAULTS); | ||
211 | out: | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static void lp8860_led_brightness_work(struct work_struct *work) | ||
216 | { | ||
217 | struct lp8860_led *led = container_of(work, struct lp8860_led, work); | ||
218 | int ret; | ||
219 | int disp_brightness = led->brightness * 255; | ||
220 | |||
221 | mutex_lock(&led->lock); | ||
222 | |||
223 | ret = lp8860_fault_check(led); | ||
224 | if (ret) { | ||
225 | dev_err(&led->client->dev, "Cannot read/clear faults\n"); | ||
226 | goto out; | ||
227 | } | ||
228 | |||
229 | ret = regmap_write(led->regmap, LP8860_DISP_CL1_BRT_MSB, | ||
230 | (disp_brightness & 0xff00) >> 8); | ||
231 | if (ret) { | ||
232 | dev_err(&led->client->dev, "Cannot write CL1 MSB\n"); | ||
233 | goto out; | ||
234 | } | ||
235 | |||
236 | ret = regmap_write(led->regmap, LP8860_DISP_CL1_BRT_LSB, | ||
237 | disp_brightness & 0xff); | ||
238 | if (ret) { | ||
239 | dev_err(&led->client->dev, "Cannot write CL1 LSB\n"); | ||
240 | goto out; | ||
241 | } | ||
242 | out: | ||
243 | mutex_unlock(&led->lock); | ||
244 | } | ||
245 | |||
246 | static void lp8860_brightness_set(struct led_classdev *led_cdev, | ||
247 | enum led_brightness brt_val) | ||
248 | { | ||
249 | struct lp8860_led *led = | ||
250 | container_of(led_cdev, struct lp8860_led, led_dev); | ||
251 | |||
252 | led->brightness = brt_val; | ||
253 | schedule_work(&led->work); | ||
254 | } | ||
255 | |||
256 | static int lp8860_init(struct lp8860_led *led) | ||
257 | { | ||
258 | unsigned int read_buf; | ||
259 | int ret, i, reg_count; | ||
260 | |||
261 | if (led->enable_gpio) | ||
262 | gpiod_direction_output(led->enable_gpio, 1); | ||
263 | |||
264 | ret = lp8860_fault_check(led); | ||
265 | if (ret) | ||
266 | goto out; | ||
267 | |||
268 | ret = regmap_read(led->regmap, LP8860_STATUS, &read_buf); | ||
269 | if (ret) | ||
270 | goto out; | ||
271 | |||
272 | ret = lp8860_unlock_eeprom(led, LP8860_UNLOCK_EEPROM); | ||
273 | if (ret) { | ||
274 | dev_err(&led->client->dev, "Failed unlocking EEPROM\n"); | ||
275 | goto out; | ||
276 | } | ||
277 | |||
278 | reg_count = ARRAY_SIZE(lp8860_eeprom_disp_regs) / sizeof(lp8860_eeprom_disp_regs[0]); | ||
279 | for (i = 0; i < reg_count; i++) { | ||
280 | ret = regmap_write(led->eeprom_regmap, | ||
281 | lp8860_eeprom_disp_regs[i].reg, | ||
282 | lp8860_eeprom_disp_regs[i].value); | ||
283 | if (ret) { | ||
284 | dev_err(&led->client->dev, "Failed writing EEPROM\n"); | ||
285 | goto out; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | ret = lp8860_unlock_eeprom(led, LP8860_LOCK_EEPROM); | ||
290 | if (ret) | ||
291 | goto out; | ||
292 | |||
293 | ret = regmap_write(led->regmap, | ||
294 | LP8860_EEPROM_CNTRL, | ||
295 | LP8860_PROGRAM_EEPROM); | ||
296 | if (ret) | ||
297 | dev_err(&led->client->dev, "Failed programming EEPROM\n"); | ||
298 | out: | ||
299 | if (ret) | ||
300 | if (led->enable_gpio) | ||
301 | gpiod_direction_output(led->enable_gpio, 0); | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | static struct reg_default lp8860_reg_defs[] = { | ||
306 | { LP8860_DISP_CL1_BRT_MSB, 0x00}, | ||
307 | { LP8860_DISP_CL1_BRT_LSB, 0x00}, | ||
308 | { LP8860_DISP_CL1_CURR_MSB, 0x00}, | ||
309 | { LP8860_DISP_CL1_CURR_LSB, 0x00}, | ||
310 | { LP8860_CL2_BRT_MSB, 0x00}, | ||
311 | { LP8860_CL2_BRT_LSB, 0x00}, | ||
312 | { LP8860_CL2_CURRENT, 0x00}, | ||
313 | { LP8860_CL3_BRT_MSB, 0x00}, | ||
314 | { LP8860_CL3_BRT_LSB, 0x00}, | ||
315 | { LP8860_CL3_CURRENT, 0x00}, | ||
316 | { LP8860_CL4_BRT_MSB, 0x00}, | ||
317 | { LP8860_CL4_BRT_LSB, 0x00}, | ||
318 | { LP8860_CL4_CURRENT, 0x00}, | ||
319 | { LP8860_CONFIG, 0x00}, | ||
320 | { LP8860_FAULT_CLEAR, 0x00}, | ||
321 | { LP8860_EEPROM_CNTRL, 0x80}, | ||
322 | { LP8860_EEPROM_UNLOCK, 0x00}, | ||
323 | }; | ||
324 | |||
325 | static const struct regmap_config lp8860_regmap_config = { | ||
326 | .reg_bits = 8, | ||
327 | .val_bits = 8, | ||
328 | |||
329 | .max_register = LP8860_EEPROM_UNLOCK, | ||
330 | .reg_defaults = lp8860_reg_defs, | ||
331 | .num_reg_defaults = ARRAY_SIZE(lp8860_reg_defs), | ||
332 | .cache_type = REGCACHE_NONE, | ||
333 | }; | ||
334 | |||
335 | static struct reg_default lp8860_eeprom_defs[] = { | ||
336 | { LP8860_EEPROM_REG_0, 0x00 }, | ||
337 | { LP8860_EEPROM_REG_1, 0x00 }, | ||
338 | { LP8860_EEPROM_REG_2, 0x00 }, | ||
339 | { LP8860_EEPROM_REG_3, 0x00 }, | ||
340 | { LP8860_EEPROM_REG_4, 0x00 }, | ||
341 | { LP8860_EEPROM_REG_5, 0x00 }, | ||
342 | { LP8860_EEPROM_REG_6, 0x00 }, | ||
343 | { LP8860_EEPROM_REG_7, 0x00 }, | ||
344 | { LP8860_EEPROM_REG_8, 0x00 }, | ||
345 | { LP8860_EEPROM_REG_9, 0x00 }, | ||
346 | { LP8860_EEPROM_REG_10, 0x00 }, | ||
347 | { LP8860_EEPROM_REG_11, 0x00 }, | ||
348 | { LP8860_EEPROM_REG_12, 0x00 }, | ||
349 | { LP8860_EEPROM_REG_13, 0x00 }, | ||
350 | { LP8860_EEPROM_REG_14, 0x00 }, | ||
351 | { LP8860_EEPROM_REG_15, 0x00 }, | ||
352 | { LP8860_EEPROM_REG_16, 0x00 }, | ||
353 | { LP8860_EEPROM_REG_17, 0x00 }, | ||
354 | { LP8860_EEPROM_REG_18, 0x00 }, | ||
355 | { LP8860_EEPROM_REG_19, 0x00 }, | ||
356 | { LP8860_EEPROM_REG_20, 0x00 }, | ||
357 | { LP8860_EEPROM_REG_21, 0x00 }, | ||
358 | { LP8860_EEPROM_REG_22, 0x00 }, | ||
359 | { LP8860_EEPROM_REG_23, 0x00 }, | ||
360 | { LP8860_EEPROM_REG_24, 0x00 }, | ||
361 | }; | ||
362 | |||
363 | static const struct regmap_config lp8860_eeprom_regmap_config = { | ||
364 | .reg_bits = 8, | ||
365 | .val_bits = 8, | ||
366 | |||
367 | .max_register = LP8860_EEPROM_REG_24, | ||
368 | .reg_defaults = lp8860_eeprom_defs, | ||
369 | .num_reg_defaults = ARRAY_SIZE(lp8860_eeprom_defs), | ||
370 | .cache_type = REGCACHE_NONE, | ||
371 | }; | ||
372 | |||
373 | static int lp8860_probe(struct i2c_client *client, | ||
374 | const struct i2c_device_id *id) | ||
375 | { | ||
376 | int ret; | ||
377 | struct lp8860_led *led; | ||
378 | struct device_node *np = client->dev.of_node; | ||
379 | |||
380 | led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL); | ||
381 | if (!led) | ||
382 | return -ENOMEM; | ||
383 | |||
384 | led->label = LP8860_DISP_LED_NAME; | ||
385 | |||
386 | if (client->dev.of_node) { | ||
387 | ret = of_property_read_string(np, "label", &led->label); | ||
388 | if (ret) { | ||
389 | dev_err(&client->dev, "Missing label in dt\n"); | ||
390 | return -EINVAL; | ||
391 | } | ||
392 | } | ||
393 | |||
394 | led->enable_gpio = devm_gpiod_get(&client->dev, "enable"); | ||
395 | if (IS_ERR(led->enable_gpio)) | ||
396 | led->enable_gpio = NULL; | ||
397 | else | ||
398 | gpiod_direction_output(led->enable_gpio, 0); | ||
399 | |||
400 | led->regulator = devm_regulator_get(&client->dev, "vled"); | ||
401 | if (IS_ERR(led->regulator)) | ||
402 | led->regulator = NULL; | ||
403 | |||
404 | led->client = client; | ||
405 | led->led_dev.name = led->label; | ||
406 | led->led_dev.max_brightness = LED_FULL; | ||
407 | led->led_dev.brightness_set = lp8860_brightness_set; | ||
408 | |||
409 | mutex_init(&led->lock); | ||
410 | INIT_WORK(&led->work, lp8860_led_brightness_work); | ||
411 | |||
412 | i2c_set_clientdata(client, led); | ||
413 | |||
414 | led->regmap = devm_regmap_init_i2c(client, &lp8860_regmap_config); | ||
415 | if (IS_ERR(led->regmap)) { | ||
416 | ret = PTR_ERR(led->regmap); | ||
417 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | ||
418 | ret); | ||
419 | return ret; | ||
420 | } | ||
421 | |||
422 | led->eeprom_regmap = devm_regmap_init_i2c(client, &lp8860_eeprom_regmap_config); | ||
423 | if (IS_ERR(led->eeprom_regmap)) { | ||
424 | ret = PTR_ERR(led->eeprom_regmap); | ||
425 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | ||
426 | ret); | ||
427 | return ret; | ||
428 | } | ||
429 | |||
430 | ret = lp8860_init(led); | ||
431 | if (ret) | ||
432 | return ret; | ||
433 | |||
434 | ret = led_classdev_register(&client->dev, &led->led_dev); | ||
435 | if (ret) { | ||
436 | dev_err(&client->dev, "led register err: %d\n", ret); | ||
437 | return ret; | ||
438 | } | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | static int lp8860_remove(struct i2c_client *client) | ||
444 | { | ||
445 | struct lp8860_led *led = i2c_get_clientdata(client); | ||
446 | int ret; | ||
447 | |||
448 | led_classdev_unregister(&led->led_dev); | ||
449 | cancel_work_sync(&led->work); | ||
450 | |||
451 | if (led->enable_gpio) | ||
452 | gpiod_direction_output(led->enable_gpio, 0); | ||
453 | |||
454 | if (led->regulator) { | ||
455 | ret = regulator_disable(led->regulator); | ||
456 | if (ret) | ||
457 | dev_err(&led->client->dev, | ||
458 | "Failed to disable regulator\n"); | ||
459 | } | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | static const struct i2c_device_id lp8860_id[] = { | ||
465 | { "lp8860", 0 }, | ||
466 | { } | ||
467 | }; | ||
468 | MODULE_DEVICE_TABLE(i2c, lp8860_id); | ||
469 | |||
470 | #ifdef CONFIG_OF | ||
471 | static const struct of_device_id of_lp8860_leds_match[] = { | ||
472 | { .compatible = "ti,lp8860", }, | ||
473 | {}, | ||
474 | }; | ||
475 | MODULE_DEVICE_TABLE(of, of_lp8860_leds_match); | ||
476 | #endif | ||
477 | |||
478 | static struct i2c_driver lp8860_driver = { | ||
479 | .driver = { | ||
480 | .name = "lp8860", | ||
481 | .of_match_table = of_match_ptr(of_lp8860_leds_match), | ||
482 | }, | ||
483 | .probe = lp8860_probe, | ||
484 | .remove = lp8860_remove, | ||
485 | .id_table = lp8860_id, | ||
486 | }; | ||
487 | module_i2c_driver(lp8860_driver); | ||
488 | |||
489 | MODULE_DESCRIPTION("Texas Instruments LP8860 LED drvier"); | ||
490 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); | ||
491 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c index fa1126f12063..ffc21397a675 100644 --- a/drivers/leds/leds-regulator.c +++ b/drivers/leds/leds-regulator.c | |||
@@ -153,24 +153,21 @@ static int regulator_led_probe(struct platform_device *pdev) | |||
153 | return -ENODEV; | 153 | return -ENODEV; |
154 | } | 154 | } |
155 | 155 | ||
156 | vcc = regulator_get_exclusive(&pdev->dev, "vled"); | 156 | vcc = devm_regulator_get_exclusive(&pdev->dev, "vled"); |
157 | if (IS_ERR(vcc)) { | 157 | if (IS_ERR(vcc)) { |
158 | dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); | 158 | dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); |
159 | return PTR_ERR(vcc); | 159 | return PTR_ERR(vcc); |
160 | } | 160 | } |
161 | 161 | ||
162 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); | 162 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); |
163 | if (led == NULL) { | 163 | if (led == NULL) |
164 | ret = -ENOMEM; | 164 | return -ENOMEM; |
165 | goto err_vcc; | ||
166 | } | ||
167 | 165 | ||
168 | led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); | 166 | led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); |
169 | if (pdata->brightness > led->cdev.max_brightness) { | 167 | if (pdata->brightness > led->cdev.max_brightness) { |
170 | dev_err(&pdev->dev, "Invalid default brightness %d\n", | 168 | dev_err(&pdev->dev, "Invalid default brightness %d\n", |
171 | pdata->brightness); | 169 | pdata->brightness); |
172 | ret = -EINVAL; | 170 | return -EINVAL; |
173 | goto err_vcc; | ||
174 | } | 171 | } |
175 | led->value = pdata->brightness; | 172 | led->value = pdata->brightness; |
176 | 173 | ||
@@ -191,7 +188,7 @@ static int regulator_led_probe(struct platform_device *pdev) | |||
191 | ret = led_classdev_register(&pdev->dev, &led->cdev); | 188 | ret = led_classdev_register(&pdev->dev, &led->cdev); |
192 | if (ret < 0) { | 189 | if (ret < 0) { |
193 | cancel_work_sync(&led->work); | 190 | cancel_work_sync(&led->work); |
194 | goto err_vcc; | 191 | return ret; |
195 | } | 192 | } |
196 | 193 | ||
197 | /* to expose the default value to userspace */ | 194 | /* to expose the default value to userspace */ |
@@ -201,10 +198,6 @@ static int regulator_led_probe(struct platform_device *pdev) | |||
201 | regulator_led_set_value(led); | 198 | regulator_led_set_value(led); |
202 | 199 | ||
203 | return 0; | 200 | return 0; |
204 | |||
205 | err_vcc: | ||
206 | regulator_put(vcc); | ||
207 | return ret; | ||
208 | } | 201 | } |
209 | 202 | ||
210 | static int regulator_led_remove(struct platform_device *pdev) | 203 | static int regulator_led_remove(struct platform_device *pdev) |
@@ -214,7 +207,6 @@ static int regulator_led_remove(struct platform_device *pdev) | |||
214 | led_classdev_unregister(&led->cdev); | 207 | led_classdev_unregister(&led->cdev); |
215 | cancel_work_sync(&led->work); | 208 | cancel_work_sync(&led->work); |
216 | regulator_led_disable(led); | 209 | regulator_led_disable(led); |
217 | regulator_put(led->vcc); | ||
218 | return 0; | 210 | return 0; |
219 | } | 211 | } |
220 | 212 | ||
diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c index 3afec79c43f4..6896e2d9ba58 100644 --- a/drivers/leds/leds-syscon.c +++ b/drivers/leds/leds-syscon.c | |||
@@ -18,10 +18,6 @@ | |||
18 | * along with this program; if not, write to the Free Software | 18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
20 | * MA 02111-1307 USA | 20 | * MA 02111-1307 USA |
21 | * | ||
22 | * This driver provides system reboot functionality for APM X-Gene SoC. | ||
23 | * For system shutdown, this is board specify. If a board designer | ||
24 | * implements GPIO shutdown, use the gpio-poweroff.c driver. | ||
25 | */ | 21 | */ |
26 | #include <linux/io.h> | 22 | #include <linux/io.h> |
27 | #include <linux/of_device.h> | 23 | #include <linux/of_device.h> |
@@ -70,39 +66,13 @@ static void syscon_led_set(struct led_classdev *led_cdev, | |||
70 | dev_err(sled->cdev.dev, "error updating LED status\n"); | 66 | dev_err(sled->cdev.dev, "error updating LED status\n"); |
71 | } | 67 | } |
72 | 68 | ||
73 | static const struct of_device_id syscon_match[] = { | 69 | static int __init syscon_leds_spawn(struct device_node *np, |
74 | { .compatible = "syscon", }, | 70 | struct device *dev, |
75 | {}, | 71 | struct regmap *map) |
76 | }; | ||
77 | |||
78 | static int __init syscon_leds_init(void) | ||
79 | { | 72 | { |
80 | const struct of_device_id *devid; | ||
81 | struct device_node *np; | ||
82 | struct device_node *child; | 73 | struct device_node *child; |
83 | struct regmap *map; | ||
84 | struct platform_device *pdev; | ||
85 | struct device *dev; | ||
86 | int ret; | 74 | int ret; |
87 | 75 | ||
88 | np = of_find_matching_node_and_match(NULL, syscon_match, | ||
89 | &devid); | ||
90 | if (!np) | ||
91 | return -ENODEV; | ||
92 | |||
93 | map = syscon_node_to_regmap(np); | ||
94 | if (IS_ERR(map)) | ||
95 | return PTR_ERR(map); | ||
96 | |||
97 | /* | ||
98 | * If the map is there, the device should be there, we allocate | ||
99 | * memory on the syscon device's behalf here. | ||
100 | */ | ||
101 | pdev = of_find_device_by_node(np); | ||
102 | if (!pdev) | ||
103 | return -ENODEV; | ||
104 | dev = &pdev->dev; | ||
105 | |||
106 | for_each_available_child_of_node(np, child) { | 76 | for_each_available_child_of_node(np, child) { |
107 | struct syscon_led *sled; | 77 | struct syscon_led *sled; |
108 | const char *state; | 78 | const char *state; |
@@ -150,7 +120,6 @@ static int __init syscon_leds_init(void) | |||
150 | if (ret < 0) | 120 | if (ret < 0) |
151 | return ret; | 121 | return ret; |
152 | } | 122 | } |
153 | |||
154 | } | 123 | } |
155 | sled->cdev.brightness_set = syscon_led_set; | 124 | sled->cdev.brightness_set = syscon_led_set; |
156 | 125 | ||
@@ -160,7 +129,39 @@ static int __init syscon_leds_init(void) | |||
160 | 129 | ||
161 | dev_info(dev, "registered LED %s\n", sled->cdev.name); | 130 | dev_info(dev, "registered LED %s\n", sled->cdev.name); |
162 | } | 131 | } |
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static int __init syscon_leds_init(void) | ||
136 | { | ||
137 | struct device_node *np; | ||
138 | |||
139 | for_each_of_allnodes(np) { | ||
140 | struct platform_device *pdev; | ||
141 | struct regmap *map; | ||
142 | int ret; | ||
143 | |||
144 | if (!of_device_is_compatible(np, "syscon")) | ||
145 | continue; | ||
146 | |||
147 | map = syscon_node_to_regmap(np); | ||
148 | if (IS_ERR(map)) { | ||
149 | pr_err("error getting regmap for syscon LEDs\n"); | ||
150 | continue; | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * If the map is there, the device should be there, we allocate | ||
155 | * memory on the syscon device's behalf here. | ||
156 | */ | ||
157 | pdev = of_find_device_by_node(np); | ||
158 | if (!pdev) | ||
159 | return -ENODEV; | ||
160 | ret = syscon_leds_spawn(np, &pdev->dev, map); | ||
161 | if (ret) | ||
162 | dev_err(&pdev->dev, "could not spawn syscon LEDs\n"); | ||
163 | } | ||
163 | 164 | ||
164 | return 0; | 165 | return 0; |
165 | } | 166 | } |
166 | device_initcall(syscon_leds_init); | 167 | device_initcall(syscon_leds_init); |
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 4c50365344a9..2348dbda5269 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h | |||
@@ -17,16 +17,28 @@ | |||
17 | #include <linux/rwsem.h> | 17 | #include <linux/rwsem.h> |
18 | #include <linux/leds.h> | 18 | #include <linux/leds.h> |
19 | 19 | ||
20 | static inline void __led_set_brightness(struct led_classdev *led_cdev, | 20 | static inline void led_set_brightness_async(struct led_classdev *led_cdev, |
21 | enum led_brightness value) | 21 | enum led_brightness value) |
22 | { | 22 | { |
23 | if (value > led_cdev->max_brightness) | 23 | led_cdev->brightness = min(value, led_cdev->max_brightness); |
24 | value = led_cdev->max_brightness; | 24 | |
25 | led_cdev->brightness = value; | ||
26 | if (!(led_cdev->flags & LED_SUSPENDED)) | 25 | if (!(led_cdev->flags & LED_SUSPENDED)) |
27 | led_cdev->brightness_set(led_cdev, value); | 26 | led_cdev->brightness_set(led_cdev, value); |
28 | } | 27 | } |
29 | 28 | ||
29 | static inline int led_set_brightness_sync(struct led_classdev *led_cdev, | ||
30 | enum led_brightness value) | ||
31 | { | ||
32 | int ret = 0; | ||
33 | |||
34 | led_cdev->brightness = min(value, led_cdev->max_brightness); | ||
35 | |||
36 | if (!(led_cdev->flags & LED_SUSPENDED)) | ||
37 | ret = led_cdev->brightness_set_sync(led_cdev, | ||
38 | led_cdev->brightness); | ||
39 | return ret; | ||
40 | } | ||
41 | |||
30 | static inline int led_get_brightness(struct led_classdev *led_cdev) | 42 | static inline int led_get_brightness(struct led_classdev *led_cdev) |
31 | { | 43 | { |
32 | return led_cdev->brightness; | 44 | return led_cdev->brightness; |
diff --git a/drivers/leds/trigger/ledtrig-backlight.c b/drivers/leds/trigger/ledtrig-backlight.c index 47e55aa9eefa..59eca17d9661 100644 --- a/drivers/leds/trigger/ledtrig-backlight.c +++ b/drivers/leds/trigger/ledtrig-backlight.c | |||
@@ -51,9 +51,9 @@ static int fb_notifier_callback(struct notifier_block *p, | |||
51 | 51 | ||
52 | if ((n->old_status == UNBLANK) ^ n->invert) { | 52 | if ((n->old_status == UNBLANK) ^ n->invert) { |
53 | n->brightness = led->brightness; | 53 | n->brightness = led->brightness; |
54 | __led_set_brightness(led, LED_OFF); | 54 | led_set_brightness_async(led, LED_OFF); |
55 | } else { | 55 | } else { |
56 | __led_set_brightness(led, n->brightness); | 56 | led_set_brightness_async(led, n->brightness); |
57 | } | 57 | } |
58 | 58 | ||
59 | n->old_status = new_status; | 59 | n->old_status = new_status; |
@@ -89,9 +89,9 @@ static ssize_t bl_trig_invert_store(struct device *dev, | |||
89 | 89 | ||
90 | /* After inverting, we need to update the LED. */ | 90 | /* After inverting, we need to update the LED. */ |
91 | if ((n->old_status == BLANK) ^ n->invert) | 91 | if ((n->old_status == BLANK) ^ n->invert) |
92 | __led_set_brightness(led, LED_OFF); | 92 | led_set_brightness_async(led, LED_OFF); |
93 | else | 93 | else |
94 | __led_set_brightness(led, n->brightness); | 94 | led_set_brightness_async(led, n->brightness); |
95 | 95 | ||
96 | return num; | 96 | return num; |
97 | } | 97 | } |
diff --git a/drivers/leds/trigger/ledtrig-default-on.c b/drivers/leds/trigger/ledtrig-default-on.c index 81a91be8e18d..6f38f883aaf1 100644 --- a/drivers/leds/trigger/ledtrig-default-on.c +++ b/drivers/leds/trigger/ledtrig-default-on.c | |||
@@ -19,7 +19,7 @@ | |||
19 | 19 | ||
20 | static void defon_trig_activate(struct led_classdev *led_cdev) | 20 | static void defon_trig_activate(struct led_classdev *led_cdev) |
21 | { | 21 | { |
22 | __led_set_brightness(led_cdev, led_cdev->max_brightness); | 22 | led_set_brightness_async(led_cdev, led_cdev->max_brightness); |
23 | } | 23 | } |
24 | 24 | ||
25 | static struct led_trigger defon_led_trigger = { | 25 | static struct led_trigger defon_led_trigger = { |
diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c index c86c41826476..4cc7040746c6 100644 --- a/drivers/leds/trigger/ledtrig-gpio.c +++ b/drivers/leds/trigger/ledtrig-gpio.c | |||
@@ -54,12 +54,12 @@ static void gpio_trig_work(struct work_struct *work) | |||
54 | 54 | ||
55 | if (tmp) { | 55 | if (tmp) { |
56 | if (gpio_data->desired_brightness) | 56 | if (gpio_data->desired_brightness) |
57 | __led_set_brightness(gpio_data->led, | 57 | led_set_brightness_async(gpio_data->led, |
58 | gpio_data->desired_brightness); | 58 | gpio_data->desired_brightness); |
59 | else | 59 | else |
60 | __led_set_brightness(gpio_data->led, LED_FULL); | 60 | led_set_brightness_async(gpio_data->led, LED_FULL); |
61 | } else { | 61 | } else { |
62 | __led_set_brightness(gpio_data->led, LED_OFF); | 62 | led_set_brightness_async(gpio_data->led, LED_OFF); |
63 | } | 63 | } |
64 | } | 64 | } |
65 | 65 | ||
diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c index 5c8464a33172..fea6871d2609 100644 --- a/drivers/leds/trigger/ledtrig-heartbeat.c +++ b/drivers/leds/trigger/ledtrig-heartbeat.c | |||
@@ -74,7 +74,7 @@ static void led_heartbeat_function(unsigned long data) | |||
74 | break; | 74 | break; |
75 | } | 75 | } |
76 | 76 | ||
77 | __led_set_brightness(led_cdev, brightness); | 77 | led_set_brightness_async(led_cdev, brightness); |
78 | mod_timer(&heartbeat_data->timer, jiffies + delay); | 78 | mod_timer(&heartbeat_data->timer, jiffies + delay); |
79 | } | 79 | } |
80 | 80 | ||
diff --git a/drivers/leds/trigger/ledtrig-oneshot.c b/drivers/leds/trigger/ledtrig-oneshot.c index cb4c7466692a..fbd02cdc3ad7 100644 --- a/drivers/leds/trigger/ledtrig-oneshot.c +++ b/drivers/leds/trigger/ledtrig-oneshot.c | |||
@@ -63,9 +63,9 @@ static ssize_t led_invert_store(struct device *dev, | |||
63 | oneshot_data->invert = !!state; | 63 | oneshot_data->invert = !!state; |
64 | 64 | ||
65 | if (oneshot_data->invert) | 65 | if (oneshot_data->invert) |
66 | __led_set_brightness(led_cdev, LED_FULL); | 66 | led_set_brightness_async(led_cdev, LED_FULL); |
67 | else | 67 | else |
68 | __led_set_brightness(led_cdev, LED_OFF); | 68 | led_set_brightness_async(led_cdev, LED_OFF); |
69 | 69 | ||
70 | return size; | 70 | return size; |
71 | } | 71 | } |
diff --git a/drivers/leds/trigger/ledtrig-transient.c b/drivers/leds/trigger/ledtrig-transient.c index e5abc00bb00c..3c34de404d18 100644 --- a/drivers/leds/trigger/ledtrig-transient.c +++ b/drivers/leds/trigger/ledtrig-transient.c | |||
@@ -41,7 +41,7 @@ static void transient_timer_function(unsigned long data) | |||
41 | struct transient_trig_data *transient_data = led_cdev->trigger_data; | 41 | struct transient_trig_data *transient_data = led_cdev->trigger_data; |
42 | 42 | ||
43 | transient_data->activate = 0; | 43 | transient_data->activate = 0; |
44 | __led_set_brightness(led_cdev, transient_data->restore_state); | 44 | led_set_brightness_async(led_cdev, transient_data->restore_state); |
45 | } | 45 | } |
46 | 46 | ||
47 | static ssize_t transient_activate_show(struct device *dev, | 47 | static ssize_t transient_activate_show(struct device *dev, |
@@ -72,7 +72,8 @@ static ssize_t transient_activate_store(struct device *dev, | |||
72 | if (state == 0 && transient_data->activate == 1) { | 72 | if (state == 0 && transient_data->activate == 1) { |
73 | del_timer(&transient_data->timer); | 73 | del_timer(&transient_data->timer); |
74 | transient_data->activate = state; | 74 | transient_data->activate = state; |
75 | __led_set_brightness(led_cdev, transient_data->restore_state); | 75 | led_set_brightness_async(led_cdev, |
76 | transient_data->restore_state); | ||
76 | return size; | 77 | return size; |
77 | } | 78 | } |
78 | 79 | ||
@@ -80,7 +81,7 @@ static ssize_t transient_activate_store(struct device *dev, | |||
80 | if (state == 1 && transient_data->activate == 0 && | 81 | if (state == 1 && transient_data->activate == 0 && |
81 | transient_data->duration != 0) { | 82 | transient_data->duration != 0) { |
82 | transient_data->activate = state; | 83 | transient_data->activate = state; |
83 | __led_set_brightness(led_cdev, transient_data->state); | 84 | led_set_brightness_async(led_cdev, transient_data->state); |
84 | transient_data->restore_state = | 85 | transient_data->restore_state = |
85 | (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL; | 86 | (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL; |
86 | mod_timer(&transient_data->timer, | 87 | mod_timer(&transient_data->timer, |
@@ -203,7 +204,8 @@ static void transient_trig_deactivate(struct led_classdev *led_cdev) | |||
203 | 204 | ||
204 | if (led_cdev->activated) { | 205 | if (led_cdev->activated) { |
205 | del_timer_sync(&transient_data->timer); | 206 | del_timer_sync(&transient_data->timer); |
206 | __led_set_brightness(led_cdev, transient_data->restore_state); | 207 | led_set_brightness_async(led_cdev, |
208 | transient_data->restore_state); | ||
207 | device_remove_file(led_cdev->dev, &dev_attr_activate); | 209 | device_remove_file(led_cdev->dev, &dev_attr_activate); |
208 | device_remove_file(led_cdev->dev, &dev_attr_duration); | 210 | device_remove_file(led_cdev->dev, &dev_attr_duration); |
209 | device_remove_file(led_cdev->dev, &dev_attr_state); | 211 | device_remove_file(led_cdev->dev, &dev_attr_state); |