diff options
author | Peter Ujfalusi <peter.ujfalusi@ti.com> | 2012-12-21 04:44:01 -0500 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2013-02-01 20:47:05 -0500 |
commit | 08541cbcc7386fa78efb454a92ddbfb1a2859cf9 (patch) | |
tree | 288c9a3b4fcd689cb4551999afa39dbfa2fd0ee2 /drivers | |
parent | 8a66a579083a20172a46e74d175a57621dccae0a (diff) |
leds: leds-pwm: Add device tree bindings
The DT binding for the pwm-leds devices are similar to the gpio-leds type.
LEDs are represented as sub-nodes of the pwm-leds device.
The code for handling the DT boot is based on the code found in the
leds-gpio driver and adapted to use PWMs instead of GPIOs.
To avoid having custom cleanup code in case of DT boot the newly created
devm_of_pwm_get() API is used to get the correct PWM instance.
For usage see:
Documentation/devicetree/bindings/leds/leds-pwm.txt
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/leds/leds-pwm.c | 112 |
1 files changed, 92 insertions, 20 deletions
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 46f4e44c6c6d..a1ea5f6a8d39 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
19 | #include <linux/of_platform.h> | ||
19 | #include <linux/fb.h> | 20 | #include <linux/fb.h> |
20 | #include <linux/leds.h> | 21 | #include <linux/leds.h> |
21 | #include <linux/err.h> | 22 | #include <linux/err.h> |
@@ -58,46 +59,110 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds) | |||
58 | (sizeof(struct led_pwm_data) * num_leds); | 59 | (sizeof(struct led_pwm_data) * num_leds); |
59 | } | 60 | } |
60 | 61 | ||
61 | static int led_pwm_probe(struct platform_device *pdev) | 62 | static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev) |
62 | { | 63 | { |
63 | struct led_pwm_platform_data *pdata = pdev->dev.platform_data; | 64 | struct device_node *node = pdev->dev.of_node; |
65 | struct device_node *child; | ||
64 | struct led_pwm_priv *priv; | 66 | struct led_pwm_priv *priv; |
65 | int i, ret = 0; | 67 | int count, ret; |
66 | 68 | ||
67 | if (!pdata) | 69 | /* count LEDs in this device, so we know how much to allocate */ |
68 | return -EBUSY; | 70 | count = of_get_child_count(node); |
71 | if (!count) | ||
72 | return NULL; | ||
69 | 73 | ||
70 | priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(pdata->num_leds), | 74 | priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count), |
71 | GFP_KERNEL); | 75 | GFP_KERNEL); |
72 | if (!priv) | 76 | if (!priv) |
73 | return -ENOMEM; | 77 | return NULL; |
74 | 78 | ||
75 | for (i = 0; i < pdata->num_leds; i++) { | 79 | for_each_child_of_node(node, child) { |
76 | struct led_pwm *cur_led = &pdata->leds[i]; | 80 | struct led_pwm_data *led_dat = &priv->leds[priv->num_leds]; |
77 | struct led_pwm_data *led_dat = &priv->leds[i]; | ||
78 | 81 | ||
79 | led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); | 82 | led_dat->cdev.name = of_get_property(child, "label", |
83 | NULL) ? : child->name; | ||
84 | |||
85 | led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL); | ||
80 | if (IS_ERR(led_dat->pwm)) { | 86 | if (IS_ERR(led_dat->pwm)) { |
81 | ret = PTR_ERR(led_dat->pwm); | ||
82 | dev_err(&pdev->dev, "unable to request PWM for %s\n", | 87 | dev_err(&pdev->dev, "unable to request PWM for %s\n", |
83 | cur_led->name); | 88 | led_dat->cdev.name); |
84 | goto err; | 89 | goto err; |
85 | } | 90 | } |
91 | /* Get the period from PWM core when n*/ | ||
92 | led_dat->period = pwm_get_period(led_dat->pwm); | ||
93 | |||
94 | led_dat->cdev.default_trigger = of_get_property(child, | ||
95 | "linux,default-trigger", NULL); | ||
96 | of_property_read_u32(child, "max-brightness", | ||
97 | &led_dat->cdev.max_brightness); | ||
86 | 98 | ||
87 | led_dat->cdev.name = cur_led->name; | ||
88 | led_dat->cdev.default_trigger = cur_led->default_trigger; | ||
89 | led_dat->active_low = cur_led->active_low; | ||
90 | led_dat->period = cur_led->pwm_period_ns; | ||
91 | led_dat->cdev.brightness_set = led_pwm_set; | 99 | led_dat->cdev.brightness_set = led_pwm_set; |
92 | led_dat->cdev.brightness = LED_OFF; | 100 | led_dat->cdev.brightness = LED_OFF; |
93 | led_dat->cdev.max_brightness = cur_led->max_brightness; | ||
94 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 101 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
95 | 102 | ||
96 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | 103 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); |
97 | if (ret < 0) | 104 | if (ret < 0) { |
105 | dev_err(&pdev->dev, "failed to register for %s\n", | ||
106 | led_dat->cdev.name); | ||
107 | of_node_put(child); | ||
98 | goto err; | 108 | goto err; |
109 | } | ||
110 | priv->num_leds++; | ||
111 | } | ||
112 | |||
113 | return priv; | ||
114 | err: | ||
115 | while (priv->num_leds--) | ||
116 | led_classdev_unregister(&priv->leds[priv->num_leds].cdev); | ||
117 | |||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | static int led_pwm_probe(struct platform_device *pdev) | ||
122 | { | ||
123 | struct led_pwm_platform_data *pdata = pdev->dev.platform_data; | ||
124 | struct led_pwm_priv *priv; | ||
125 | int i, ret = 0; | ||
126 | |||
127 | if (pdata && pdata->num_leds) { | ||
128 | priv = devm_kzalloc(&pdev->dev, | ||
129 | sizeof_pwm_leds_priv(pdata->num_leds), | ||
130 | GFP_KERNEL); | ||
131 | if (!priv) | ||
132 | return -ENOMEM; | ||
133 | |||
134 | for (i = 0; i < pdata->num_leds; i++) { | ||
135 | struct led_pwm *cur_led = &pdata->leds[i]; | ||
136 | struct led_pwm_data *led_dat = &priv->leds[i]; | ||
137 | |||
138 | led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); | ||
139 | if (IS_ERR(led_dat->pwm)) { | ||
140 | ret = PTR_ERR(led_dat->pwm); | ||
141 | dev_err(&pdev->dev, | ||
142 | "unable to request PWM for %s\n", | ||
143 | cur_led->name); | ||
144 | goto err; | ||
145 | } | ||
146 | |||
147 | led_dat->cdev.name = cur_led->name; | ||
148 | led_dat->cdev.default_trigger = cur_led->default_trigger; | ||
149 | led_dat->active_low = cur_led->active_low; | ||
150 | led_dat->period = cur_led->pwm_period_ns; | ||
151 | led_dat->cdev.brightness_set = led_pwm_set; | ||
152 | led_dat->cdev.brightness = LED_OFF; | ||
153 | led_dat->cdev.max_brightness = cur_led->max_brightness; | ||
154 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
155 | |||
156 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | ||
157 | if (ret < 0) | ||
158 | goto err; | ||
159 | } | ||
160 | priv->num_leds = pdata->num_leds; | ||
161 | } else { | ||
162 | priv = led_pwm_create_of(pdev); | ||
163 | if (!priv) | ||
164 | return -ENODEV; | ||
99 | } | 165 | } |
100 | priv->num_leds = pdata->num_leds; | ||
101 | 166 | ||
102 | platform_set_drvdata(pdev, priv); | 167 | platform_set_drvdata(pdev, priv); |
103 | 168 | ||
@@ -121,12 +186,19 @@ static int led_pwm_remove(struct platform_device *pdev) | |||
121 | return 0; | 186 | return 0; |
122 | } | 187 | } |
123 | 188 | ||
189 | static const struct of_device_id of_pwm_leds_match[] = { | ||
190 | { .compatible = "pwm-leds", }, | ||
191 | {}, | ||
192 | }; | ||
193 | MODULE_DEVICE_TABLE(of, of_pwm_leds_match); | ||
194 | |||
124 | static struct platform_driver led_pwm_driver = { | 195 | static struct platform_driver led_pwm_driver = { |
125 | .probe = led_pwm_probe, | 196 | .probe = led_pwm_probe, |
126 | .remove = led_pwm_remove, | 197 | .remove = led_pwm_remove, |
127 | .driver = { | 198 | .driver = { |
128 | .name = "leds_pwm", | 199 | .name = "leds_pwm", |
129 | .owner = THIS_MODULE, | 200 | .owner = THIS_MODULE, |
201 | .of_match_table = of_match_ptr(of_pwm_leds_match), | ||
130 | }, | 202 | }, |
131 | }; | 203 | }; |
132 | 204 | ||