aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/backlight
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-07-30 12:22:37 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-30 12:22:37 -0400
commit9ec97169e7d6afe2f8206d694d1411fb3bb49853 (patch)
tree9d24c8cd440a312f96b70db5cdaaef1136787003 /drivers/video/backlight
parenta410963ba4c0c768302f0298e258b1ee940e8316 (diff)
parent19891b20e7c275feb92d669f4b1879861f7e8c25 (diff)
Merge branch 'for-3.6' of git://gitorious.org/linux-pwm/linux-pwm
Pull PWM subsystem from Thierry Reding: "The new PWM subsystem aims at collecting all implementations of the legacy PWM API and to eventually replace it completely. The subsystem has been in development for over half a year now and many drivers have already been converted. It has been in linux-next for a couple of weeks and there have been no major issues so I think it is ready for inclusion in your tree." Arnd Bergmann <arnd@arndb.de>: "Very much Ack on the new subsystem. It uses the interface declarations as the previously separate pwm drivers, so nothing changes for now in the drivers using it, although it enables us to change those more easily in the future if we want to. This work is also one of the missing pieces that are required to eventually build ARM kernels for multiple platforms, which is currently prohibited (amongs other things) by the fact that you cannot have more than one driver exporting the pwm functions." Tested-and-acked-by: Alexandre Courbot <acourbot@nvidia.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Philip, Avinash <avinashphilip@ti.com> # TI's AM33xx platforms Acked-By: Alexandre Pereira da Silva <aletes.xgr@gmail.com> # LPC32XX Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Sachin Kamat <sachin.kamat@linaro.org> Fix up trivial conflicts with other cleanups and DT updates. * 'for-3.6' of git://gitorious.org/linux-pwm/linux-pwm: (36 commits) pwm: pwm-tiehrpwm: PWM driver support for EHRPWM pwm: pwm-tiecap: PWM driver support for ECAP APWM pwm: fix used-uninitialized warning in pwm_get() pwm: add lpc32xx PWM support pwm_backlight: pass correct brightness to callback pwm: Use pr_* functions in pwm-samsung.c file pwm: Convert pwm-samsung to use devm_* APIs pwm: Convert pwm-tegra to use devm_clk_get() pwm: pwm-mxs: Return proper error if pwmchip_remove() fails pwm: pwm-bfin: Return proper error if pwmchip_remove() fails pwm: pxa: Propagate pwmchip_remove() error pwm: Convert pwm-pxa to use devm_* APIs pwm: Convert pwm-vt8500 to use devm_* APIs pwm: Convert pwm-imx to use devm_* APIs pwm: Conflict with legacy PWM API pwm: pwm-mxs: add pinctrl support pwm: pwm-mxs: use devm_* managed functions pwm: pwm-mxs: use global reset function stmp_reset_block pwm: pwm-mxs: encode soc name in compatible string pwm: Take over maintainership of the PWM subsystem ...
Diffstat (limited to 'drivers/video/backlight')
-rw-r--r--drivers/video/backlight/Kconfig2
-rw-r--r--drivers/video/backlight/pwm_bl.c159
2 files changed, 137 insertions, 24 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 2979292650d..cf282763a8d 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 342b7d7cbb6..995f0164c9b 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,9 +54,18 @@ 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 {
55 brightness = pb->lth_brightness + 57 int duty_cycle;
56 (brightness * (pb->period - pb->lth_brightness) / max); 58
57 pwm_config(pb->pwm, brightness, pb->period); 59 if (pb->levels) {
60 duty_cycle = pb->levels[brightness];
61 max = pb->levels[max];
62 } else {
63 duty_cycle = brightness;
64 }
65
66 duty_cycle = pb->lth_brightness +
67 (duty_cycle * (pb->period - pb->lth_brightness) / max);
68 pwm_config(pb->pwm, duty_cycle, pb->period);
58 pwm_enable(pb->pwm); 69 pwm_enable(pb->pwm);
59 } 70 }
60 71
@@ -83,17 +94,98 @@ static const struct backlight_ops pwm_backlight_ops = {
83 .check_fb = pwm_backlight_check_fb, 94 .check_fb = pwm_backlight_check_fb,
84}; 95};
85 96
97#ifdef CONFIG_OF
98static int pwm_backlight_parse_dt(struct device *dev,
99 struct platform_pwm_backlight_data *data)
100{
101 struct device_node *node = dev->of_node;
102 struct property *prop;
103 int length;
104 u32 value;
105 int ret;
106
107 if (!node)
108 return -ENODEV;
109
110 memset(data, 0, sizeof(*data));
111
112 /* determine the number of brightness levels */
113 prop = of_find_property(node, "brightness-levels", &length);
114 if (!prop)
115 return -EINVAL;
116
117 data->max_brightness = length / sizeof(u32);
118
119 /* read brightness levels from DT property */
120 if (data->max_brightness > 0) {
121 size_t size = sizeof(*data->levels) * data->max_brightness;
122
123 data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
124 if (!data->levels)
125 return -ENOMEM;
126
127 ret = of_property_read_u32_array(node, "brightness-levels",
128 data->levels,
129 data->max_brightness);
130 if (ret < 0)
131 return ret;
132
133 ret = of_property_read_u32(node, "default-brightness-level",
134 &value);
135 if (ret < 0)
136 return ret;
137
138 if (value >= data->max_brightness) {
139 dev_warn(dev, "invalid default brightness level: %u, using %u\n",
140 value, data->max_brightness - 1);
141 value = data->max_brightness - 1;
142 }
143
144 data->dft_brightness = value;
145 data->max_brightness--;
146 }
147
148 /*
149 * TODO: Most users of this driver use a number of GPIOs to control
150 * backlight power. Support for specifying these needs to be
151 * added.
152 */
153
154 return 0;
155}
156
157static struct of_device_id pwm_backlight_of_match[] = {
158 { .compatible = "pwm-backlight" },
159 { }
160};
161
162MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
163#else
164static int pwm_backlight_parse_dt(struct device *dev,
165 struct platform_pwm_backlight_data *data)
166{
167 return -ENODEV;
168}
169#endif
170
86static int pwm_backlight_probe(struct platform_device *pdev) 171static int pwm_backlight_probe(struct platform_device *pdev)
87{ 172{
88 struct backlight_properties props;
89 struct platform_pwm_backlight_data *data = pdev->dev.platform_data; 173 struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
174 struct platform_pwm_backlight_data defdata;
175 struct backlight_properties props;
90 struct backlight_device *bl; 176 struct backlight_device *bl;
91 struct pwm_bl_data *pb; 177 struct pwm_bl_data *pb;
178 unsigned int max;
92 int ret; 179 int ret;
93 180
94 if (!data) { 181 if (!data) {
95 dev_err(&pdev->dev, "failed to find platform data\n"); 182 ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
96 return -EINVAL; 183 if (ret < 0) {
184 dev_err(&pdev->dev, "failed to find platform data\n");
185 return ret;
186 }
187
188 data = &defdata;
97 } 189 }
98 190
99 if (data->init) { 191 if (data->init) {
@@ -109,21 +201,42 @@ static int pwm_backlight_probe(struct platform_device *pdev)
109 goto err_alloc; 201 goto err_alloc;
110 } 202 }
111 203
112 pb->period = data->pwm_period_ns; 204 if (data->levels) {
205 max = data->levels[data->max_brightness];
206 pb->levels = data->levels;
207 } else
208 max = data->max_brightness;
209
113 pb->notify = data->notify; 210 pb->notify = data->notify;
114 pb->notify_after = data->notify_after; 211 pb->notify_after = data->notify_after;
115 pb->check_fb = data->check_fb; 212 pb->check_fb = data->check_fb;
116 pb->lth_brightness = data->lth_brightness * 213 pb->exit = data->exit;
117 (data->pwm_period_ns / data->max_brightness);
118 pb->dev = &pdev->dev; 214 pb->dev = &pdev->dev;
119 215
120 pb->pwm = pwm_request(data->pwm_id, "backlight"); 216 pb->pwm = pwm_get(&pdev->dev, NULL);
121 if (IS_ERR(pb->pwm)) { 217 if (IS_ERR(pb->pwm)) {
122 dev_err(&pdev->dev, "unable to request PWM for backlight\n"); 218 dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
123 ret = PTR_ERR(pb->pwm); 219
124 goto err_alloc; 220 pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
125 } else 221 if (IS_ERR(pb->pwm)) {
126 dev_dbg(&pdev->dev, "got pwm for backlight\n"); 222 dev_err(&pdev->dev, "unable to request legacy PWM\n");
223 ret = PTR_ERR(pb->pwm);
224 goto err_alloc;
225 }
226 }
227
228 dev_dbg(&pdev->dev, "got pwm for backlight\n");
229
230 /*
231 * The DT case will set the pwm_period_ns field to 0 and store the
232 * period, parsed from the DT, in the PWM device. For the non-DT case,
233 * set the period from platform data.
234 */
235 if (data->pwm_period_ns > 0)
236 pwm_set_period(pb->pwm, data->pwm_period_ns);
237
238 pb->period = pwm_get_period(pb->pwm);
239 pb->lth_brightness = data->lth_brightness * (pb->period / max);
127 240
128 memset(&props, 0, sizeof(struct backlight_properties)); 241 memset(&props, 0, sizeof(struct backlight_properties));
129 props.type = BACKLIGHT_RAW; 242 props.type = BACKLIGHT_RAW;
@@ -143,7 +256,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
143 return 0; 256 return 0;
144 257
145err_bl: 258err_bl:
146 pwm_free(pb->pwm); 259 pwm_put(pb->pwm);
147err_alloc: 260err_alloc:
148 if (data->exit) 261 if (data->exit)
149 data->exit(&pdev->dev); 262 data->exit(&pdev->dev);
@@ -152,16 +265,15 @@ err_alloc:
152 265
153static int pwm_backlight_remove(struct platform_device *pdev) 266static int pwm_backlight_remove(struct platform_device *pdev)
154{ 267{
155 struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
156 struct backlight_device *bl = platform_get_drvdata(pdev); 268 struct backlight_device *bl = platform_get_drvdata(pdev);
157 struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); 269 struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
158 270
159 backlight_device_unregister(bl); 271 backlight_device_unregister(bl);
160 pwm_config(pb->pwm, 0, pb->period); 272 pwm_config(pb->pwm, 0, pb->period);
161 pwm_disable(pb->pwm); 273 pwm_disable(pb->pwm);
162 pwm_free(pb->pwm); 274 pwm_put(pb->pwm);
163 if (data->exit) 275 if (pb->exit)
164 data->exit(&pdev->dev); 276 pb->exit(&pdev->dev);
165 return 0; 277 return 0;
166} 278}
167 279
@@ -195,11 +307,12 @@ static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
195 307
196static struct platform_driver pwm_backlight_driver = { 308static struct platform_driver pwm_backlight_driver = {
197 .driver = { 309 .driver = {
198 .name = "pwm-backlight", 310 .name = "pwm-backlight",
199 .owner = THIS_MODULE, 311 .owner = THIS_MODULE,
200#ifdef CONFIG_PM 312#ifdef CONFIG_PM
201 .pm = &pwm_backlight_pm_ops, 313 .pm = &pwm_backlight_pm_ops,
202#endif 314#endif
315 .of_match_table = of_match_ptr(pwm_backlight_of_match),
203 }, 316 },
204 .probe = pwm_backlight_probe, 317 .probe = pwm_backlight_probe,
205 .remove = pwm_backlight_remove, 318 .remove = pwm_backlight_remove,