aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Reding <thierry.reding@avionic-design.de>2011-12-16 15:25:29 -0500
committerThierry Reding <thierry.reding@avionic-design.de>2012-07-23 07:23:52 -0400
commit3e3ed6cdc49d758719c148a78c8b04c243ef74d0 (patch)
tree4898c961d49967089139c79ff222441a4495dca4
parent4dce82c1e84007d38747533673c2289bfc6497d2 (diff)
pwm-backlight: Add rudimentary device tree support
This commit adds very basic support for device tree probing. Currently, only a PWM and a list of distinct brightness levels can be specified. Enabling or disabling backlight power via GPIOs is not yet supported. Reviewed-by: Shawn Guo <shawn.guo@linaro.org> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
-rw-r--r--Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt28
-rw-r--r--drivers/video/backlight/Kconfig2
-rw-r--r--drivers/video/backlight/pwm_bl.c149
-rw-r--r--include/linux/pwm_backlight.h1
4 files changed, 159 insertions, 21 deletions
diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
new file mode 100644
index 000000000000..1e4fc727f3b1
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
@@ -0,0 +1,28 @@
1pwm-backlight bindings
2
3Required properties:
4 - compatible: "pwm-backlight"
5 - pwms: OF device-tree PWM specification (see PWM binding[0])
6 - brightness-levels: Array of distinct brightness levels. Typically these
7 are in the range from 0 to 255, but any range starting at 0 will do.
8 The actual brightness level (PWM duty cycle) will be interpolated
9 from these values. 0 means a 0% duty cycle (darkest/off), while the
10 last value in the array represents a 100% duty cycle (brightest).
11 - default-brightness-level: the default brightness level (index into the
12 array defined by the "brightness-levels" property)
13
14Optional properties:
15 - pwm-names: a list of names for the PWM devices specified in the
16 "pwms" property (see PWM binding[0])
17
18[0]: Documentation/devicetree/bindings/pwm/pwm.txt
19
20Example:
21
22 backlight {
23 compatible = "pwm-backlight";
24 pwms = <&pwm 0 5000000>;
25
26 brightness-levels = <0 4 8 16 32 64 128 255>;
27 default-brightness-level = <6>;
28 };
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index fa2b03750316..4c9c02216351 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -245,7 +245,7 @@ config BACKLIGHT_CARILLO_RANCH
245 245
246config BACKLIGHT_PWM 246config BACKLIGHT_PWM
247 tristate "Generic PWM based Backlight Driver" 247 tristate "Generic PWM based Backlight Driver"
248 depends on HAVE_PWM 248 depends on PWM
249 help 249 help
250 If you have a LCD backlight adjustable by PWM, say Y to enable 250 If you have a LCD backlight adjustable by PWM, say Y to enable
251 this driver. 251 this driver.
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 342b7d7cbb63..057389d69a51 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -26,11 +26,13 @@ struct pwm_bl_data {
26 struct device *dev; 26 struct device *dev;
27 unsigned int period; 27 unsigned int period;
28 unsigned int lth_brightness; 28 unsigned int lth_brightness;
29 unsigned int *levels;
29 int (*notify)(struct device *, 30 int (*notify)(struct device *,
30 int brightness); 31 int brightness);
31 void (*notify_after)(struct device *, 32 void (*notify_after)(struct device *,
32 int brightness); 33 int brightness);
33 int (*check_fb)(struct device *, struct fb_info *); 34 int (*check_fb)(struct device *, struct fb_info *);
35 void (*exit)(struct device *);
34}; 36};
35 37
36static int pwm_backlight_update_status(struct backlight_device *bl) 38static int pwm_backlight_update_status(struct backlight_device *bl)
@@ -52,6 +54,11 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
52 pwm_config(pb->pwm, 0, pb->period); 54 pwm_config(pb->pwm, 0, pb->period);
53 pwm_disable(pb->pwm); 55 pwm_disable(pb->pwm);
54 } else { 56 } else {
57 if (pb->levels) {
58 brightness = pb->levels[brightness];
59 max = pb->levels[max];
60 }
61
55 brightness = pb->lth_brightness + 62 brightness = pb->lth_brightness +
56 (brightness * (pb->period - pb->lth_brightness) / max); 63 (brightness * (pb->period - pb->lth_brightness) / max);
57 pwm_config(pb->pwm, brightness, pb->period); 64 pwm_config(pb->pwm, brightness, pb->period);
@@ -83,17 +90,98 @@ static const struct backlight_ops pwm_backlight_ops = {
83 .check_fb = pwm_backlight_check_fb, 90 .check_fb = pwm_backlight_check_fb,
84}; 91};
85 92
93#ifdef CONFIG_OF
94static int pwm_backlight_parse_dt(struct device *dev,
95 struct platform_pwm_backlight_data *data)
96{
97 struct device_node *node = dev->of_node;
98 struct property *prop;
99 int length;
100 u32 value;
101 int ret;
102
103 if (!node)
104 return -ENODEV;
105
106 memset(data, 0, sizeof(*data));
107
108 /* determine the number of brightness levels */
109 prop = of_find_property(node, "brightness-levels", &length);
110 if (!prop)
111 return -EINVAL;
112
113 data->max_brightness = length / sizeof(u32);
114
115 /* read brightness levels from DT property */
116 if (data->max_brightness > 0) {
117 size_t size = sizeof(*data->levels) * data->max_brightness;
118
119 data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
120 if (!data->levels)
121 return -ENOMEM;
122
123 ret = of_property_read_u32_array(node, "brightness-levels",
124 data->levels,
125 data->max_brightness);
126 if (ret < 0)
127 return ret;
128
129 ret = of_property_read_u32(node, "default-brightness-level",
130 &value);
131 if (ret < 0)
132 return ret;
133
134 if (value >= data->max_brightness) {
135 dev_warn(dev, "invalid default brightness level: %u, using %u\n",
136 value, data->max_brightness - 1);
137 value = data->max_brightness - 1;
138 }
139
140 data->dft_brightness = value;
141 data->max_brightness--;
142 }
143
144 /*
145 * TODO: Most users of this driver use a number of GPIOs to control
146 * backlight power. Support for specifying these needs to be
147 * added.
148 */
149
150 return 0;
151}
152
153static struct of_device_id pwm_backlight_of_match[] = {
154 { .compatible = "pwm-backlight" },
155 { }
156};
157
158MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
159#else
160static int pwm_backlight_parse_dt(struct device *dev,
161 struct platform_pwm_backlight_data *data)
162{
163 return -ENODEV;
164}
165#endif
166
86static int pwm_backlight_probe(struct platform_device *pdev) 167static int pwm_backlight_probe(struct platform_device *pdev)
87{ 168{
88 struct backlight_properties props;
89 struct platform_pwm_backlight_data *data = pdev->dev.platform_data; 169 struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
170 struct platform_pwm_backlight_data defdata;
171 struct backlight_properties props;
90 struct backlight_device *bl; 172 struct backlight_device *bl;
91 struct pwm_bl_data *pb; 173 struct pwm_bl_data *pb;
174 unsigned int max;
92 int ret; 175 int ret;
93 176
94 if (!data) { 177 if (!data) {
95 dev_err(&pdev->dev, "failed to find platform data\n"); 178 ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
96 return -EINVAL; 179 if (ret < 0) {
180 dev_err(&pdev->dev, "failed to find platform data\n");
181 return ret;
182 }
183
184 data = &defdata;
97 } 185 }
98 186
99 if (data->init) { 187 if (data->init) {
@@ -109,21 +197,42 @@ static int pwm_backlight_probe(struct platform_device *pdev)
109 goto err_alloc; 197 goto err_alloc;
110 } 198 }
111 199
112 pb->period = data->pwm_period_ns; 200 if (data->levels) {
201 max = data->levels[data->max_brightness];
202 pb->levels = data->levels;
203 } else
204 max = data->max_brightness;
205
113 pb->notify = data->notify; 206 pb->notify = data->notify;
114 pb->notify_after = data->notify_after; 207 pb->notify_after = data->notify_after;
115 pb->check_fb = data->check_fb; 208 pb->check_fb = data->check_fb;
116 pb->lth_brightness = data->lth_brightness * 209 pb->exit = data->exit;
117 (data->pwm_period_ns / data->max_brightness);
118 pb->dev = &pdev->dev; 210 pb->dev = &pdev->dev;
119 211
120 pb->pwm = pwm_request(data->pwm_id, "backlight"); 212 pb->pwm = pwm_get(&pdev->dev, NULL);
121 if (IS_ERR(pb->pwm)) { 213 if (IS_ERR(pb->pwm)) {
122 dev_err(&pdev->dev, "unable to request PWM for backlight\n"); 214 dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
123 ret = PTR_ERR(pb->pwm); 215
124 goto err_alloc; 216 pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
125 } else 217 if (IS_ERR(pb->pwm)) {
126 dev_dbg(&pdev->dev, "got pwm for backlight\n"); 218 dev_err(&pdev->dev, "unable to request legacy PWM\n");
219 ret = PTR_ERR(pb->pwm);
220 goto err_alloc;
221 }
222 }
223
224 dev_dbg(&pdev->dev, "got pwm for backlight\n");
225
226 /*
227 * The DT case will set the pwm_period_ns field to 0 and store the
228 * period, parsed from the DT, in the PWM device. For the non-DT case,
229 * set the period from platform data.
230 */
231 if (data->pwm_period_ns > 0)
232 pwm_set_period(pb->pwm, data->pwm_period_ns);
233
234 pb->period = pwm_get_period(pb->pwm);
235 pb->lth_brightness = data->lth_brightness * (pb->period / max);
127 236
128 memset(&props, 0, sizeof(struct backlight_properties)); 237 memset(&props, 0, sizeof(struct backlight_properties));
129 props.type = BACKLIGHT_RAW; 238 props.type = BACKLIGHT_RAW;
@@ -143,7 +252,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
143 return 0; 252 return 0;
144 253
145err_bl: 254err_bl:
146 pwm_free(pb->pwm); 255 pwm_put(pb->pwm);
147err_alloc: 256err_alloc:
148 if (data->exit) 257 if (data->exit)
149 data->exit(&pdev->dev); 258 data->exit(&pdev->dev);
@@ -152,16 +261,15 @@ err_alloc:
152 261
153static int pwm_backlight_remove(struct platform_device *pdev) 262static int pwm_backlight_remove(struct platform_device *pdev)
154{ 263{
155 struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
156 struct backlight_device *bl = platform_get_drvdata(pdev); 264 struct backlight_device *bl = platform_get_drvdata(pdev);
157 struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); 265 struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
158 266
159 backlight_device_unregister(bl); 267 backlight_device_unregister(bl);
160 pwm_config(pb->pwm, 0, pb->period); 268 pwm_config(pb->pwm, 0, pb->period);
161 pwm_disable(pb->pwm); 269 pwm_disable(pb->pwm);
162 pwm_free(pb->pwm); 270 pwm_put(pb->pwm);
163 if (data->exit) 271 if (pb->exit)
164 data->exit(&pdev->dev); 272 pb->exit(&pdev->dev);
165 return 0; 273 return 0;
166} 274}
167 275
@@ -195,11 +303,12 @@ static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
195 303
196static struct platform_driver pwm_backlight_driver = { 304static struct platform_driver pwm_backlight_driver = {
197 .driver = { 305 .driver = {
198 .name = "pwm-backlight", 306 .name = "pwm-backlight",
199 .owner = THIS_MODULE, 307 .owner = THIS_MODULE,
200#ifdef CONFIG_PM 308#ifdef CONFIG_PM
201 .pm = &pwm_backlight_pm_ops, 309 .pm = &pwm_backlight_pm_ops,
202#endif 310#endif
311 .of_match_table = of_match_ptr(pwm_backlight_of_match),
203 }, 312 },
204 .probe = pwm_backlight_probe, 313 .probe = pwm_backlight_probe,
205 .remove = pwm_backlight_remove, 314 .remove = pwm_backlight_remove,
diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h
index 63d2df43e61a..56f4a866539a 100644
--- a/include/linux/pwm_backlight.h
+++ b/include/linux/pwm_backlight.h
@@ -12,6 +12,7 @@ struct platform_pwm_backlight_data {
12 unsigned int dft_brightness; 12 unsigned int dft_brightness;
13 unsigned int lth_brightness; 13 unsigned int lth_brightness;
14 unsigned int pwm_period_ns; 14 unsigned int pwm_period_ns;
15 unsigned int *levels;
15 int (*init)(struct device *dev); 16 int (*init)(struct device *dev);
16 int (*notify)(struct device *dev, int brightness); 17 int (*notify)(struct device *dev, int brightness);
17 void (*notify_after)(struct device *dev, int brightness); 18 void (*notify_after)(struct device *dev, int brightness);