aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Vaussard <florian.vaussard@epfl.ch>2013-01-28 09:00:59 -0500
committerBryan Wu <cooloney@gmail.com>2013-04-01 14:04:50 -0400
commitc971ff185f6443e834686f140ba6d6e341ced600 (patch)
treefef36e139acb29b2797c94861ee763817440b8dd
parent24d321284745cbc593fba8115585329d48703704 (diff)
leds: leds-pwm: Defer led_pwm_set() if PWM can sleep
Call to led_pwm_set() can happen inside atomic context, like triggers. If the PWM call can sleep, defer using a worker. Signed-off-by: Florian Vaussard <florian.vaussard@epfl.ch> Reviewed-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Acked-by: Thierry Reding <thierry.reding@avionic-design.de> Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r--drivers/leds/leds-pwm.c50
1 files changed, 42 insertions, 8 deletions
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index a1ea5f6a8d39..faf52c005e8c 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -23,12 +23,16 @@
23#include <linux/pwm.h> 23#include <linux/pwm.h>
24#include <linux/leds_pwm.h> 24#include <linux/leds_pwm.h>
25#include <linux/slab.h> 25#include <linux/slab.h>
26#include <linux/workqueue.h>
26 27
27struct led_pwm_data { 28struct led_pwm_data {
28 struct led_classdev cdev; 29 struct led_classdev cdev;
29 struct pwm_device *pwm; 30 struct pwm_device *pwm;
31 struct work_struct work;
30 unsigned int active_low; 32 unsigned int active_low;
31 unsigned int period; 33 unsigned int period;
34 int duty;
35 bool can_sleep;
32}; 36};
33 37
34struct led_pwm_priv { 38struct led_pwm_priv {
@@ -36,6 +40,26 @@ struct led_pwm_priv {
36 struct led_pwm_data leds[0]; 40 struct led_pwm_data leds[0];
37}; 41};
38 42
43static void __led_pwm_set(struct led_pwm_data *led_dat)
44{
45 int new_duty = led_dat->duty;
46
47 pwm_config(led_dat->pwm, new_duty, led_dat->period);
48
49 if (new_duty == 0)
50 pwm_disable(led_dat->pwm);
51 else
52 pwm_enable(led_dat->pwm);
53}
54
55static void led_pwm_work(struct work_struct *work)
56{
57 struct led_pwm_data *led_dat =
58 container_of(work, struct led_pwm_data, work);
59
60 __led_pwm_set(led_dat);
61}
62
39static void led_pwm_set(struct led_classdev *led_cdev, 63static void led_pwm_set(struct led_classdev *led_cdev,
40 enum led_brightness brightness) 64 enum led_brightness brightness)
41{ 65{
@@ -44,13 +68,12 @@ static void led_pwm_set(struct led_classdev *led_cdev,
44 unsigned int max = led_dat->cdev.max_brightness; 68 unsigned int max = led_dat->cdev.max_brightness;
45 unsigned int period = led_dat->period; 69 unsigned int period = led_dat->period;
46 70
47 if (brightness == 0) { 71 led_dat->duty = brightness * period / max;
48 pwm_config(led_dat->pwm, 0, period); 72
49 pwm_disable(led_dat->pwm); 73 if (led_dat->can_sleep)
50 } else { 74 schedule_work(&led_dat->work);
51 pwm_config(led_dat->pwm, brightness * period / max, period); 75 else
52 pwm_enable(led_dat->pwm); 76 __led_pwm_set(led_dat);
53 }
54} 77}
55 78
56static inline size_t sizeof_pwm_leds_priv(int num_leds) 79static inline size_t sizeof_pwm_leds_priv(int num_leds)
@@ -100,6 +123,10 @@ static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev)
100 led_dat->cdev.brightness = LED_OFF; 123 led_dat->cdev.brightness = LED_OFF;
101 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 124 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
102 125
126 led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
127 if (led_dat->can_sleep)
128 INIT_WORK(&led_dat->work, led_pwm_work);
129
103 ret = led_classdev_register(&pdev->dev, &led_dat->cdev); 130 ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
104 if (ret < 0) { 131 if (ret < 0) {
105 dev_err(&pdev->dev, "failed to register for %s\n", 132 dev_err(&pdev->dev, "failed to register for %s\n",
@@ -153,6 +180,10 @@ static int led_pwm_probe(struct platform_device *pdev)
153 led_dat->cdev.max_brightness = cur_led->max_brightness; 180 led_dat->cdev.max_brightness = cur_led->max_brightness;
154 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 181 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
155 182
183 led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
184 if (led_dat->can_sleep)
185 INIT_WORK(&led_dat->work, led_pwm_work);
186
156 ret = led_classdev_register(&pdev->dev, &led_dat->cdev); 187 ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
157 if (ret < 0) 188 if (ret < 0)
158 goto err; 189 goto err;
@@ -180,8 +211,11 @@ static int led_pwm_remove(struct platform_device *pdev)
180 struct led_pwm_priv *priv = platform_get_drvdata(pdev); 211 struct led_pwm_priv *priv = platform_get_drvdata(pdev);
181 int i; 212 int i;
182 213
183 for (i = 0; i < priv->num_leds; i++) 214 for (i = 0; i < priv->num_leds; i++) {
184 led_classdev_unregister(&priv->leds[i].cdev); 215 led_classdev_unregister(&priv->leds[i].cdev);
216 if (priv->leds[i].can_sleep)
217 cancel_work_sync(&priv->leds[i].work);
218 }
185 219
186 return 0; 220 return 0;
187} 221}