blob: 105f199a656dddfe0663582f96f5fd02d5a3ec57 [file] [log] [blame]
eric miao42796d32008-04-14 09:35:08 +01001/*
2 * linux/drivers/video/backlight/pwm_bl.c
3 *
4 * simple PWM based backlight control, board code has to setup
5 * 1) pin configuration so PWM waveforms can output
Eric Miaob8cdd872009-02-10 13:30:37 +08006 * 2) platform_data being correctly configured
eric miao42796d32008-04-14 09:35:08 +01007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
Enric Balletbo i Serra31576942018-03-28 19:03:25 +020013#include <linux/delay.h>
Alexandre Courbot257462d2014-02-27 14:53:34 +090014#include <linux/gpio/consumer.h>
Thierry Reding8265b2e2013-08-30 12:32:18 +020015#include <linux/gpio.h>
eric miao42796d32008-04-14 09:35:08 +010016#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/init.h>
19#include <linux/platform_device.h>
20#include <linux/fb.h>
21#include <linux/backlight.h>
22#include <linux/err.h>
23#include <linux/pwm.h>
24#include <linux/pwm_backlight.h>
Thierry Reding22ceeee2013-08-30 12:38:34 +020025#include <linux/regulator/consumer.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
eric miao42796d32008-04-14 09:35:08 +010027
28struct pwm_bl_data {
29 struct pwm_device *pwm;
Ben Dookscfc38992009-11-10 17:20:40 +000030 struct device *dev;
eric miao42796d32008-04-14 09:35:08 +010031 unsigned int period;
Arun Murthyfef77642010-11-11 14:05:28 -080032 unsigned int lth_brightness;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +010033 unsigned int *levels;
Thierry Reding97c38432013-10-02 18:01:02 +020034 bool enabled;
Thierry Reding22ceeee2013-08-30 12:38:34 +020035 struct regulator *power_supply;
Alexandre Courbot257462d2014-02-27 14:53:34 +090036 struct gpio_desc *enable_gpio;
Mike Dunn8f43e182013-09-22 09:59:56 -070037 unsigned int scale;
Vladimir Zapolskiyedf387b2014-10-11 16:46:26 +030038 bool legacy;
Enric Balletbo i Serra31576942018-03-28 19:03:25 +020039 unsigned int post_pwm_on_delay;
40 unsigned int pwm_off_delay;
Ben Dookscfc38992009-11-10 17:20:40 +000041 int (*notify)(struct device *,
42 int brightness);
Dilan Leecc7993f2011-08-25 15:59:17 -070043 void (*notify_after)(struct device *,
44 int brightness);
Robert Morellef0a5e82011-03-22 16:30:31 -070045 int (*check_fb)(struct device *, struct fb_info *);
Thierry Reding3e3ed6c2011-12-16 21:25:29 +010046 void (*exit)(struct device *);
eric miao42796d32008-04-14 09:35:08 +010047};
48
Mike Dunn8f43e182013-09-22 09:59:56 -070049static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
Thierry Reding62b744a2013-10-07 11:32:02 +020050{
Thierry Reding73d4e2b2013-10-22 09:37:05 +020051 int err;
Thierry Reding62b744a2013-10-07 11:32:02 +020052
Thierry Reding97c38432013-10-02 18:01:02 +020053 if (pb->enabled)
54 return;
55
Thierry Reding22ceeee2013-08-30 12:38:34 +020056 err = regulator_enable(pb->power_supply);
57 if (err < 0)
58 dev_err(pb->dev, "failed to enable power supply\n");
59
Enric Balletbo i Serra5fb5cae2018-03-28 19:03:23 +020060 pwm_enable(pb->pwm);
61
Enric Balletbo i Serra31576942018-03-28 19:03:25 +020062 if (pb->post_pwm_on_delay)
63 msleep(pb->post_pwm_on_delay);
64
Alexandre Courbot257462d2014-02-27 14:53:34 +090065 if (pb->enable_gpio)
Maxime Ripard0c9501f2016-08-31 10:18:12 +020066 gpiod_set_value_cansleep(pb->enable_gpio, 1);
Thierry Reding8265b2e2013-08-30 12:32:18 +020067
Thierry Reding97c38432013-10-02 18:01:02 +020068 pb->enabled = true;
Thierry Reding62b744a2013-10-07 11:32:02 +020069}
70
71static void pwm_backlight_power_off(struct pwm_bl_data *pb)
72{
Thierry Reding97c38432013-10-02 18:01:02 +020073 if (!pb->enabled)
74 return;
75
Alexandre Courbot257462d2014-02-27 14:53:34 +090076 if (pb->enable_gpio)
Maxime Ripard0c9501f2016-08-31 10:18:12 +020077 gpiod_set_value_cansleep(pb->enable_gpio, 0);
Thierry Reding8265b2e2013-08-30 12:32:18 +020078
Enric Balletbo i Serra31576942018-03-28 19:03:25 +020079 if (pb->pwm_off_delay)
80 msleep(pb->pwm_off_delay);
81
Enric Balletbo i Serra5fb5cae2018-03-28 19:03:23 +020082 pwm_config(pb->pwm, 0, pb->period);
83 pwm_disable(pb->pwm);
84
Thierry Reding22ceeee2013-08-30 12:38:34 +020085 regulator_disable(pb->power_supply);
Thierry Reding97c38432013-10-02 18:01:02 +020086 pb->enabled = false;
Thierry Reding62b744a2013-10-07 11:32:02 +020087}
88
Thierry Redinge4bfeda2013-10-18 10:46:24 +020089static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
90{
91 unsigned int lth = pb->lth_brightness;
Derek Basehore5d0c49a2017-08-29 13:34:34 -070092 u64 duty_cycle;
Thierry Redinge4bfeda2013-10-18 10:46:24 +020093
94 if (pb->levels)
95 duty_cycle = pb->levels[brightness];
96 else
97 duty_cycle = brightness;
98
Derek Basehore5d0c49a2017-08-29 13:34:34 -070099 duty_cycle *= pb->period - lth;
100 do_div(duty_cycle, pb->scale);
101
102 return duty_cycle + lth;
Thierry Redinge4bfeda2013-10-18 10:46:24 +0200103}
104
eric miao42796d32008-04-14 09:35:08 +0100105static int pwm_backlight_update_status(struct backlight_device *bl)
106{
Jingoo Hane6e3dbf2013-02-21 16:43:46 -0800107 struct pwm_bl_data *pb = bl_get_data(bl);
eric miao42796d32008-04-14 09:35:08 +0100108 int brightness = bl->props.brightness;
Thierry Redinge4bfeda2013-10-18 10:46:24 +0200109 int duty_cycle;
eric miao42796d32008-04-14 09:35:08 +0100110
Alexandre Courbot01322672013-01-30 15:19:15 +0900111 if (bl->props.power != FB_BLANK_UNBLANK ||
112 bl->props.fb_blank != FB_BLANK_UNBLANK ||
113 bl->props.state & BL_CORE_FBBLANK)
eric miao42796d32008-04-14 09:35:08 +0100114 brightness = 0;
115
Philipp Zabel3b731252008-05-22 14:18:40 +0100116 if (pb->notify)
Ben Dookscfc38992009-11-10 17:20:40 +0000117 brightness = pb->notify(pb->dev, brightness);
Philipp Zabel3b731252008-05-22 14:18:40 +0100118
Thierry Redinge4bfeda2013-10-18 10:46:24 +0200119 if (brightness > 0) {
120 duty_cycle = compute_duty_cycle(pb, brightness);
Alexandre Courbot9fb978b2012-07-09 15:04:23 +0900121 pwm_config(pb->pwm, duty_cycle, pb->period);
Mike Dunn8f43e182013-09-22 09:59:56 -0700122 pwm_backlight_power_on(pb, brightness);
Thierry Redinge4bfeda2013-10-18 10:46:24 +0200123 } else
Thierry Reding62b744a2013-10-07 11:32:02 +0200124 pwm_backlight_power_off(pb);
Dilan Leecc7993f2011-08-25 15:59:17 -0700125
126 if (pb->notify_after)
127 pb->notify_after(pb->dev, brightness);
128
eric miao42796d32008-04-14 09:35:08 +0100129 return 0;
130}
131
Robert Morellef0a5e82011-03-22 16:30:31 -0700132static int pwm_backlight_check_fb(struct backlight_device *bl,
133 struct fb_info *info)
134{
Jingoo Hane6e3dbf2013-02-21 16:43:46 -0800135 struct pwm_bl_data *pb = bl_get_data(bl);
Robert Morellef0a5e82011-03-22 16:30:31 -0700136
137 return !pb->check_fb || pb->check_fb(pb->dev, info);
138}
139
Emese Revfy9905a432009-12-14 00:58:57 +0100140static const struct backlight_ops pwm_backlight_ops = {
eric miao42796d32008-04-14 09:35:08 +0100141 .update_status = pwm_backlight_update_status,
Robert Morellef0a5e82011-03-22 16:30:31 -0700142 .check_fb = pwm_backlight_check_fb,
eric miao42796d32008-04-14 09:35:08 +0100143};
144
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100145#ifdef CONFIG_OF
146static int pwm_backlight_parse_dt(struct device *dev,
147 struct platform_pwm_backlight_data *data)
148{
149 struct device_node *node = dev->of_node;
Enric Balletbo i Serra573fe6d2018-04-09 10:33:30 +0200150 unsigned int num_levels = 0;
151 unsigned int levels_count;
152 unsigned int num_steps;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100153 struct property *prop;
Enric Balletbo i Serra573fe6d2018-04-09 10:33:30 +0200154 unsigned int *table;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100155 int length;
156 u32 value;
157 int ret;
158
159 if (!node)
160 return -ENODEV;
161
162 memset(data, 0, sizeof(*data));
163
164 /* determine the number of brightness levels */
165 prop = of_find_property(node, "brightness-levels", &length);
166 if (!prop)
167 return -EINVAL;
168
169 data->max_brightness = length / sizeof(u32);
170
171 /* read brightness levels from DT property */
172 if (data->max_brightness > 0) {
173 size_t size = sizeof(*data->levels) * data->max_brightness;
Enric Balletbo i Serra573fe6d2018-04-09 10:33:30 +0200174 unsigned int i, j, n = 0;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100175
176 data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
177 if (!data->levels)
178 return -ENOMEM;
179
180 ret = of_property_read_u32_array(node, "brightness-levels",
181 data->levels,
182 data->max_brightness);
183 if (ret < 0)
184 return ret;
185
186 ret = of_property_read_u32(node, "default-brightness-level",
187 &value);
188 if (ret < 0)
189 return ret;
190
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100191 data->dft_brightness = value;
Enric Balletbo i Serra573fe6d2018-04-09 10:33:30 +0200192
193 /*
194 * This property is optional, if is set enables linear
195 * interpolation between each of the values of brightness levels
196 * and creates a new pre-computed table.
197 */
198 of_property_read_u32(node, "num-interpolated-steps",
199 &num_steps);
200
201 /*
202 * Make sure that there is at least two entries in the
203 * brightness-levels table, otherwise we can't interpolate
204 * between two points.
205 */
206 if (num_steps) {
207 if (data->max_brightness < 2) {
208 dev_err(dev, "can't interpolate\n");
209 return -EINVAL;
210 }
211
212 /*
213 * Recalculate the number of brightness levels, now
214 * taking in consideration the number of interpolated
215 * steps between two levels.
216 */
217 for (i = 0; i < data->max_brightness - 1; i++) {
218 if ((data->levels[i + 1] - data->levels[i]) /
219 num_steps)
220 num_levels += num_steps;
221 else
222 num_levels++;
223 }
224 num_levels++;
225 dev_dbg(dev, "new number of brightness levels: %d\n",
226 num_levels);
227
228 /*
229 * Create a new table of brightness levels with all the
230 * interpolated steps.
231 */
232 size = sizeof(*table) * num_levels;
233 table = devm_kzalloc(dev, size, GFP_KERNEL);
234 if (!table)
235 return -ENOMEM;
236
237 /* Fill the interpolated table. */
238 levels_count = 0;
239 for (i = 0; i < data->max_brightness - 1; i++) {
240 value = data->levels[i];
241 n = (data->levels[i + 1] - value) / num_steps;
242 if (n > 0) {
243 for (j = 0; j < num_steps; j++) {
244 table[levels_count] = value;
245 value += n;
246 levels_count++;
247 }
248 } else {
249 table[levels_count] = data->levels[i];
250 levels_count++;
251 }
252 }
253 table[levels_count] = data->levels[i];
254
255 /*
256 * As we use interpolation lets remove current
257 * brightness levels table and replace for the
258 * new interpolated table.
259 */
260 devm_kfree(dev, data->levels);
261 data->levels = table;
262
263 /*
264 * Reassign max_brightness value to the new total number
265 * of brightness levels.
266 */
267 data->max_brightness = num_levels;
268 }
269
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100270 data->max_brightness--;
271 }
272
Enric Balletbo i Serra31576942018-03-28 19:03:25 +0200273 /*
274 * These values are optional and set as 0 by default, the out values
275 * are modified only if a valid u32 value can be decoded.
276 */
277 of_property_read_u32(node, "post-pwm-on-delay-ms",
278 &data->post_pwm_on_delay);
279 of_property_read_u32(node, "pwm-off-delay-ms", &data->pwm_off_delay);
280
Lothar Waßmann937222c2014-08-20 08:38:36 +0200281 data->enable_gpio = -EINVAL;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100282 return 0;
283}
284
Arvind Yadav62cdfe62017-06-20 13:22:15 +0530285static const struct of_device_id pwm_backlight_of_match[] = {
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100286 { .compatible = "pwm-backlight" },
287 { }
288};
289
290MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
291#else
292static int pwm_backlight_parse_dt(struct device *dev,
293 struct platform_pwm_backlight_data *data)
294{
295 return -ENODEV;
296}
297#endif
298
Peter Ujfalusi7613c922016-11-22 15:41:22 +0200299static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
300{
301 struct device_node *node = pb->dev->of_node;
302
303 /* Not booted with device tree or no phandle link to the node */
304 if (!node || !node->phandle)
305 return FB_BLANK_UNBLANK;
306
307 /*
308 * If the driver is probed from the device tree and there is a
309 * phandle link pointing to the backlight node, it is safe to
310 * assume that another driver will enable the backlight at the
311 * appropriate time. Therefore, if it is disabled, keep it so.
312 */
313
314 /* if the enable GPIO is disabled, do not enable the backlight */
315 if (pb->enable_gpio && gpiod_get_value(pb->enable_gpio) == 0)
316 return FB_BLANK_POWERDOWN;
317
318 /* The regulator is disabled, do not enable the backlight */
319 if (!regulator_is_enabled(pb->power_supply))
320 return FB_BLANK_POWERDOWN;
321
Peter Ujfalusid1b81292016-11-22 15:41:23 +0200322 /* The PWM is disabled, keep it like this */
323 if (!pwm_is_enabled(pb->pwm))
324 return FB_BLANK_POWERDOWN;
325
Peter Ujfalusi7613c922016-11-22 15:41:22 +0200326 return FB_BLANK_UNBLANK;
327}
328
eric miao42796d32008-04-14 09:35:08 +0100329static int pwm_backlight_probe(struct platform_device *pdev)
330{
Jingoo Hanc512794c2013-11-12 15:09:04 -0800331 struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100332 struct platform_pwm_backlight_data defdata;
333 struct backlight_properties props;
eric miao42796d32008-04-14 09:35:08 +0100334 struct backlight_device *bl;
Philipp Zabel87770782015-12-10 10:09:06 +0100335 struct device_node *node = pdev->dev.of_node;
eric miao42796d32008-04-14 09:35:08 +0100336 struct pwm_bl_data *pb;
Boris Brezillon6cb96442016-04-14 21:17:29 +0200337 struct pwm_args pargs;
Philipp Zabel3b731252008-05-22 14:18:40 +0100338 int ret;
eric miao42796d32008-04-14 09:35:08 +0100339
Ben Dooks14563a42008-08-05 13:01:22 -0700340 if (!data) {
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100341 ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
342 if (ret < 0) {
343 dev_err(&pdev->dev, "failed to find platform data\n");
344 return ret;
345 }
346
347 data = &defdata;
Ben Dooks14563a42008-08-05 13:01:22 -0700348 }
eric miao42796d32008-04-14 09:35:08 +0100349
Philipp Zabel3b731252008-05-22 14:18:40 +0100350 if (data->init) {
351 ret = data->init(&pdev->dev);
352 if (ret < 0)
353 return ret;
354 }
355
Julia Lawallce969222012-03-23 15:02:00 -0700356 pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
Philipp Zabel3b731252008-05-22 14:18:40 +0100357 if (!pb) {
358 ret = -ENOMEM;
359 goto err_alloc;
360 }
eric miao42796d32008-04-14 09:35:08 +0100361
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100362 if (data->levels) {
Mike Dunn8f43e182013-09-22 09:59:56 -0700363 unsigned int i;
364
365 for (i = 0; i <= data->max_brightness; i++)
366 if (data->levels[i] > pb->scale)
367 pb->scale = data->levels[i];
368
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100369 pb->levels = data->levels;
370 } else
Mike Dunn8f43e182013-09-22 09:59:56 -0700371 pb->scale = data->max_brightness;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100372
Philipp Zabel3b731252008-05-22 14:18:40 +0100373 pb->notify = data->notify;
Dilan Leecc7993f2011-08-25 15:59:17 -0700374 pb->notify_after = data->notify_after;
Robert Morellef0a5e82011-03-22 16:30:31 -0700375 pb->check_fb = data->check_fb;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100376 pb->exit = data->exit;
Ben Dookscfc38992009-11-10 17:20:40 +0000377 pb->dev = &pdev->dev;
Thierry Reding97c38432013-10-02 18:01:02 +0200378 pb->enabled = false;
Enric Balletbo i Serra31576942018-03-28 19:03:25 +0200379 pb->post_pwm_on_delay = data->post_pwm_on_delay;
380 pb->pwm_off_delay = data->pwm_off_delay;
eric miao42796d32008-04-14 09:35:08 +0100381
Axel Lincdaefcc2015-05-16 22:08:10 +0800382 pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
Philipp Zabel3698d7e2015-11-18 18:12:25 +0100383 GPIOD_ASIS);
Alexandre Courbot257462d2014-02-27 14:53:34 +0900384 if (IS_ERR(pb->enable_gpio)) {
385 ret = PTR_ERR(pb->enable_gpio);
Alexandre Courbotff9c5422014-06-25 18:18:18 +0900386 goto err_alloc;
Alexandre Courbot257462d2014-02-27 14:53:34 +0900387 }
Thierry Reding8265b2e2013-08-30 12:32:18 +0200388
Alexandre Courbot257462d2014-02-27 14:53:34 +0900389 /*
390 * Compatibility fallback for drivers still using the integer GPIO
391 * platform data. Must go away soon.
392 */
393 if (!pb->enable_gpio && gpio_is_valid(data->enable_gpio)) {
394 ret = devm_gpio_request_one(&pdev->dev, data->enable_gpio,
395 GPIOF_OUT_INIT_HIGH, "enable");
Thierry Reding8265b2e2013-08-30 12:32:18 +0200396 if (ret < 0) {
397 dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
Alexandre Courbot257462d2014-02-27 14:53:34 +0900398 data->enable_gpio, ret);
Thierry Reding8265b2e2013-08-30 12:32:18 +0200399 goto err_alloc;
400 }
Alexandre Courbot257462d2014-02-27 14:53:34 +0900401
402 pb->enable_gpio = gpio_to_desc(data->enable_gpio);
Thierry Reding8265b2e2013-08-30 12:32:18 +0200403 }
404
Peter Ujfalusi7613c922016-11-22 15:41:22 +0200405 /*
Geert Uytterhoeven892c7782017-04-04 12:54:35 +0200406 * If the GPIO is not known to be already configured as output, that
Wolfram Sangbb084c02018-01-14 22:07:10 +0100407 * is, if gpiod_get_direction returns either 1 or -EINVAL, change the
408 * direction to output and set the GPIO as active.
Peter Ujfalusi7613c922016-11-22 15:41:22 +0200409 * Do not force the GPIO to active when it was already output as it
410 * could cause backlight flickering or we would enable the backlight too
411 * early. Leave the decision of the initial backlight state for later.
412 */
413 if (pb->enable_gpio &&
Wolfram Sangbb084c02018-01-14 22:07:10 +0100414 gpiod_get_direction(pb->enable_gpio) != 0)
Peter Ujfalusi7613c922016-11-22 15:41:22 +0200415 gpiod_direction_output(pb->enable_gpio, 1);
Philipp Zabel3698d7e2015-11-18 18:12:25 +0100416
Thierry Reding22ceeee2013-08-30 12:38:34 +0200417 pb->power_supply = devm_regulator_get(&pdev->dev, "power");
418 if (IS_ERR(pb->power_supply)) {
419 ret = PTR_ERR(pb->power_supply);
Alexandre Courbot257462d2014-02-27 14:53:34 +0900420 goto err_alloc;
Thierry Reding22ceeee2013-08-30 12:38:34 +0200421 }
eric miao42796d32008-04-14 09:35:08 +0100422
Sachin Kamat60ce7022012-09-17 11:50:47 +0530423 pb->pwm = devm_pwm_get(&pdev->dev, NULL);
Philipp Zabel87770782015-12-10 10:09:06 +0100424 if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER && !node) {
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100425 dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
Vladimir Zapolskiyedf387b2014-10-11 16:46:26 +0300426 pb->legacy = true;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100427 pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
Vladimir Zapolskiydc881122015-10-12 15:29:03 +0300428 }
429
430 if (IS_ERR(pb->pwm)) {
431 ret = PTR_ERR(pb->pwm);
432 if (ret != -EPROBE_DEFER)
433 dev_err(&pdev->dev, "unable to request PWM\n");
434 goto err_alloc;
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100435 }
436
437 dev_dbg(&pdev->dev, "got pwm for backlight\n");
438
439 /*
Boris Brezillon6cb96442016-04-14 21:17:29 +0200440 * FIXME: pwm_apply_args() should be removed when switching to
441 * the atomic PWM API.
442 */
443 pwm_apply_args(pb->pwm);
444
445 /*
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100446 * The DT case will set the pwm_period_ns field to 0 and store the
447 * period, parsed from the DT, in the PWM device. For the non-DT case,
Alexandre Belloni16fc3352014-05-19 22:42:42 +0200448 * set the period from platform data if it has not already been set
449 * via the PWM lookup table.
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100450 */
Boris Brezillon6cb96442016-04-14 21:17:29 +0200451 pwm_get_args(pb->pwm, &pargs);
452 pb->period = pargs.period;
Boris BREZILLON7f044b02016-03-30 22:03:25 +0200453 if (!pb->period && (data->pwm_period_ns > 0))
Alexandre Belloni16fc3352014-05-19 22:42:42 +0200454 pb->period = data->pwm_period_ns;
Alexandre Belloni16fc3352014-05-19 22:42:42 +0200455
Mike Dunn8f43e182013-09-22 09:59:56 -0700456 pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
eric miao42796d32008-04-14 09:35:08 +0100457
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500458 memset(&props, 0, sizeof(struct backlight_properties));
Matthew Garrettbb7ca742011-03-22 16:30:21 -0700459 props.type = BACKLIGHT_RAW;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500460 props.max_brightness = data->max_brightness;
461 bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
462 &pwm_backlight_ops, &props);
eric miao42796d32008-04-14 09:35:08 +0100463 if (IS_ERR(bl)) {
464 dev_err(&pdev->dev, "failed to register backlight\n");
Philipp Zabel3b731252008-05-22 14:18:40 +0100465 ret = PTR_ERR(bl);
Vladimir Zapolskiy60d613d2015-06-14 17:32:14 +0300466 if (pb->legacy)
467 pwm_free(pb->pwm);
Alexandre Courbot257462d2014-02-27 14:53:34 +0900468 goto err_alloc;
eric miao42796d32008-04-14 09:35:08 +0100469 }
470
Peter Ujfalusi83cfd722013-01-22 14:39:54 +0100471 if (data->dft_brightness > data->max_brightness) {
472 dev_warn(&pdev->dev,
473 "invalid default brightness level: %u, using %u\n",
474 data->dft_brightness, data->max_brightness);
475 data->dft_brightness = data->max_brightness;
476 }
477
eric miao42796d32008-04-14 09:35:08 +0100478 bl->props.brightness = data->dft_brightness;
Peter Ujfalusi7613c922016-11-22 15:41:22 +0200479 bl->props.power = pwm_backlight_initial_power_state(pb);
eric miao42796d32008-04-14 09:35:08 +0100480 backlight_update_status(bl);
481
482 platform_set_drvdata(pdev, bl);
483 return 0;
Philipp Zabel3b731252008-05-22 14:18:40 +0100484
Philipp Zabel3b731252008-05-22 14:18:40 +0100485err_alloc:
486 if (data->exit)
487 data->exit(&pdev->dev);
488 return ret;
eric miao42796d32008-04-14 09:35:08 +0100489}
490
491static int pwm_backlight_remove(struct platform_device *pdev)
492{
493 struct backlight_device *bl = platform_get_drvdata(pdev);
Jingoo Hane6e3dbf2013-02-21 16:43:46 -0800494 struct pwm_bl_data *pb = bl_get_data(bl);
eric miao42796d32008-04-14 09:35:08 +0100495
496 backlight_device_unregister(bl);
Thierry Reding62b744a2013-10-07 11:32:02 +0200497 pwm_backlight_power_off(pb);
Thierry Reding668e63c2013-10-07 11:30:50 +0200498
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100499 if (pb->exit)
500 pb->exit(&pdev->dev);
Vladimir Zapolskiyedf387b2014-10-11 16:46:26 +0300501 if (pb->legacy)
502 pwm_free(pb->pwm);
Thierry Reding668e63c2013-10-07 11:30:50 +0200503
eric miao42796d32008-04-14 09:35:08 +0100504 return 0;
505}
506
Thierry Reding5f33b892014-04-29 17:28:59 +0200507static void pwm_backlight_shutdown(struct platform_device *pdev)
508{
509 struct backlight_device *bl = platform_get_drvdata(pdev);
510 struct pwm_bl_data *pb = bl_get_data(bl);
511
512 pwm_backlight_power_off(pb);
513}
514
Jingoo Hanc7911262013-02-25 17:15:47 +0900515#ifdef CONFIG_PM_SLEEP
Mark Browne2c17bc2012-01-10 15:09:22 -0800516static int pwm_backlight_suspend(struct device *dev)
eric miao42796d32008-04-14 09:35:08 +0100517{
Mark Browne2c17bc2012-01-10 15:09:22 -0800518 struct backlight_device *bl = dev_get_drvdata(dev);
Jingoo Hane6e3dbf2013-02-21 16:43:46 -0800519 struct pwm_bl_data *pb = bl_get_data(bl);
eric miao42796d32008-04-14 09:35:08 +0100520
Marc Zyngier82e8b542009-06-19 10:50:54 +0200521 if (pb->notify)
Ben Dookscfc38992009-11-10 17:20:40 +0000522 pb->notify(pb->dev, 0);
Thierry Reding668e63c2013-10-07 11:30:50 +0200523
Thierry Reding62b744a2013-10-07 11:32:02 +0200524 pwm_backlight_power_off(pb);
Thierry Reding668e63c2013-10-07 11:30:50 +0200525
Dilan Leecc7993f2011-08-25 15:59:17 -0700526 if (pb->notify_after)
527 pb->notify_after(pb->dev, 0);
Thierry Reding668e63c2013-10-07 11:30:50 +0200528
eric miao42796d32008-04-14 09:35:08 +0100529 return 0;
530}
531
Mark Browne2c17bc2012-01-10 15:09:22 -0800532static int pwm_backlight_resume(struct device *dev)
eric miao42796d32008-04-14 09:35:08 +0100533{
Mark Browne2c17bc2012-01-10 15:09:22 -0800534 struct backlight_device *bl = dev_get_drvdata(dev);
eric miao42796d32008-04-14 09:35:08 +0100535
536 backlight_update_status(bl);
Thierry Reding668e63c2013-10-07 11:30:50 +0200537
eric miao42796d32008-04-14 09:35:08 +0100538 return 0;
539}
Jingoo Hanc7911262013-02-25 17:15:47 +0900540#endif
Mark Browne2c17bc2012-01-10 15:09:22 -0800541
Huayi Li1dea1fd2013-10-09 10:33:02 +0800542static const struct dev_pm_ops pwm_backlight_pm_ops = {
543#ifdef CONFIG_PM_SLEEP
544 .suspend = pwm_backlight_suspend,
545 .resume = pwm_backlight_resume,
546 .poweroff = pwm_backlight_suspend,
547 .restore = pwm_backlight_resume,
548#endif
549};
Mark Browne2c17bc2012-01-10 15:09:22 -0800550
eric miao42796d32008-04-14 09:35:08 +0100551static struct platform_driver pwm_backlight_driver = {
552 .driver = {
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100553 .name = "pwm-backlight",
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100554 .pm = &pwm_backlight_pm_ops,
Thierry Reding3e3ed6c2011-12-16 21:25:29 +0100555 .of_match_table = of_match_ptr(pwm_backlight_of_match),
eric miao42796d32008-04-14 09:35:08 +0100556 },
557 .probe = pwm_backlight_probe,
558 .remove = pwm_backlight_remove,
Thierry Reding5f33b892014-04-29 17:28:59 +0200559 .shutdown = pwm_backlight_shutdown,
eric miao42796d32008-04-14 09:35:08 +0100560};
561
Axel Lin81178e02012-01-10 15:09:11 -0800562module_platform_driver(pwm_backlight_driver);
eric miao42796d32008-04-14 09:35:08 +0100563
564MODULE_DESCRIPTION("PWM based Backlight Driver");
565MODULE_LICENSE("GPL");
Ben Dooks8cd68192008-08-05 13:01:24 -0700566MODULE_ALIAS("platform:pwm-backlight");