xref: /openbmc/linux/drivers/video/backlight/pwm_bl.c (revision b181f7029bd71238ac2754ce7052dffd69432085)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
242796d37Seric miao /*
35076fbedSAndy Shevchenko  * Simple PWM based backlight control, board code has to setup
442796d37Seric miao  * 1) pin configuration so PWM waveforms can output
5b8cdd877SEric Miao  * 2) platform_data being correctly configured
642796d37Seric miao  */
742796d37Seric miao 
83157694dSEnric Balletbo i Serra #include <linux/delay.h>
9257462dbSAlexandre Courbot #include <linux/gpio/consumer.h>
1042796d37Seric miao #include <linux/module.h>
1142796d37Seric miao #include <linux/kernel.h>
1242796d37Seric miao #include <linux/init.h>
1342796d37Seric miao #include <linux/platform_device.h>
1442796d37Seric miao #include <linux/fb.h>
1542796d37Seric miao #include <linux/backlight.h>
1642796d37Seric miao #include <linux/err.h>
1742796d37Seric miao #include <linux/pwm.h>
1842796d37Seric miao #include <linux/pwm_backlight.h>
1922ceeee1SThierry Reding #include <linux/regulator/consumer.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
2142796d37Seric miao 
2242796d37Seric miao struct pwm_bl_data {
2342796d37Seric miao 	struct pwm_device	*pwm;
24cfc3899fSBen Dooks 	struct device		*dev;
25fef7764fSArun Murthy 	unsigned int		lth_brightness;
263e3ed6cdSThierry Reding 	unsigned int		*levels;
27e4c8ae3eSHeiko Stuebner 	bool			enabled;
2822ceeee1SThierry Reding 	struct regulator	*power_supply;
29257462dbSAlexandre Courbot 	struct gpio_desc	*enable_gpio;
308f43e18eSMike Dunn 	unsigned int		scale;
313157694dSEnric Balletbo i Serra 	unsigned int		post_pwm_on_delay;
323157694dSEnric Balletbo i Serra 	unsigned int		pwm_off_delay;
33cfc3899fSBen Dooks 	int			(*notify)(struct device *,
34cfc3899fSBen Dooks 					  int brightness);
35cc7993f6SDilan Lee 	void			(*notify_after)(struct device *,
36cc7993f6SDilan Lee 					int brightness);
37ef0a5e80SRobert Morell 	int			(*check_fb)(struct device *, struct fb_info *);
383e3ed6cdSThierry Reding 	void			(*exit)(struct device *);
3942796d37Seric miao };
4042796d37Seric miao 
pwm_backlight_power_on(struct pwm_bl_data * pb)41e6bcca08SEnric Balletbo i Serra static void pwm_backlight_power_on(struct pwm_bl_data *pb)
4262b744a8SThierry Reding {
4373d4e2b8SThierry Reding 	int err;
4462b744a8SThierry Reding 
45e4c8ae3eSHeiko Stuebner 	if (pb->enabled)
4697c38437SThierry Reding 		return;
4797c38437SThierry Reding 
48deaeeda2SUwe Kleine-König 	if (pb->power_supply) {
4922ceeee1SThierry Reding 		err = regulator_enable(pb->power_supply);
5022ceeee1SThierry Reding 		if (err < 0)
5122ceeee1SThierry Reding 			dev_err(pb->dev, "failed to enable power supply\n");
52deaeeda2SUwe Kleine-König 	}
5322ceeee1SThierry Reding 
543157694dSEnric Balletbo i Serra 	if (pb->post_pwm_on_delay)
553157694dSEnric Balletbo i Serra 		msleep(pb->post_pwm_on_delay);
563157694dSEnric Balletbo i Serra 
570c9501f8SMaxime Ripard 	gpiod_set_value_cansleep(pb->enable_gpio, 1);
58e4c8ae3eSHeiko Stuebner 
59e4c8ae3eSHeiko Stuebner 	pb->enabled = true;
6062b744a8SThierry Reding }
6162b744a8SThierry Reding 
pwm_backlight_power_off(struct pwm_bl_data * pb)6262b744a8SThierry Reding static void pwm_backlight_power_off(struct pwm_bl_data *pb)
6362b744a8SThierry Reding {
64e4c8ae3eSHeiko Stuebner 	if (!pb->enabled)
6597c38437SThierry Reding 		return;
6697c38437SThierry Reding 
670c9501f8SMaxime Ripard 	gpiod_set_value_cansleep(pb->enable_gpio, 0);
688265b2e4SThierry Reding 
693157694dSEnric Balletbo i Serra 	if (pb->pwm_off_delay)
703157694dSEnric Balletbo i Serra 		msleep(pb->pwm_off_delay);
713157694dSEnric Balletbo i Serra 
72deaeeda2SUwe Kleine-König 	if (pb->power_supply)
7322ceeee1SThierry Reding 		regulator_disable(pb->power_supply);
74e4c8ae3eSHeiko Stuebner 	pb->enabled = false;
7562b744a8SThierry Reding }
7662b744a8SThierry Reding 
compute_duty_cycle(struct pwm_bl_data * pb,int brightness,struct pwm_state * state)7700e7e698SUwe Kleine-König static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness, struct pwm_state *state)
78e4bfeda9SThierry Reding {
79e4bfeda9SThierry Reding 	unsigned int lth = pb->lth_brightness;
805d0c49acSDerek Basehore 	u64 duty_cycle;
81e4bfeda9SThierry Reding 
82e4bfeda9SThierry Reding 	if (pb->levels)
83e4bfeda9SThierry Reding 		duty_cycle = pb->levels[brightness];
84e4bfeda9SThierry Reding 	else
85e4bfeda9SThierry Reding 		duty_cycle = brightness;
86e4bfeda9SThierry Reding 
8700e7e698SUwe Kleine-König 	duty_cycle *= state->period - lth;
885d0c49acSDerek Basehore 	do_div(duty_cycle, pb->scale);
895d0c49acSDerek Basehore 
905d0c49acSDerek Basehore 	return duty_cycle + lth;
91e4bfeda9SThierry Reding }
92e4bfeda9SThierry Reding 
pwm_backlight_update_status(struct backlight_device * bl)9342796d37Seric miao static int pwm_backlight_update_status(struct backlight_device *bl)
9442796d37Seric miao {
95e6e3dbf9SJingoo Han 	struct pwm_bl_data *pb = bl_get_data(bl);
9651d53e5bSSam Ravnborg 	int brightness = backlight_get_brightness(bl);
97e6bcca08SEnric Balletbo i Serra 	struct pwm_state state;
9842796d37Seric miao 
993b73125aSPhilipp Zabel 	if (pb->notify)
100cfc3899fSBen Dooks 		brightness = pb->notify(pb->dev, brightness);
1013b73125aSPhilipp Zabel 
102e4bfeda9SThierry Reding 	if (brightness > 0) {
103e6bcca08SEnric Balletbo i Serra 		pwm_get_state(pb->pwm, &state);
10400e7e698SUwe Kleine-König 		state.duty_cycle = compute_duty_cycle(pb, brightness, &state);
10500e7e698SUwe Kleine-König 		state.enabled = true;
106*a10c3d5fSSean Young 		pwm_apply_might_sleep(pb->pwm, &state);
10700e7e698SUwe Kleine-König 
108e6bcca08SEnric Balletbo i Serra 		pwm_backlight_power_on(pb);
109349ee122SMatthias Kaehlcke 	} else {
11062b744a8SThierry Reding 		pwm_backlight_power_off(pb);
11100e7e698SUwe Kleine-König 
11200e7e698SUwe Kleine-König 		pwm_get_state(pb->pwm, &state);
11300e7e698SUwe Kleine-König 		state.duty_cycle = 0;
114deaeeda2SUwe Kleine-König 		/*
115deaeeda2SUwe Kleine-König 		 * We cannot assume a disabled PWM to drive its output to the
116deaeeda2SUwe Kleine-König 		 * inactive state. If we have an enable GPIO and/or a regulator
117deaeeda2SUwe Kleine-König 		 * we assume that this isn't relevant and we can disable the PWM
118deaeeda2SUwe Kleine-König 		 * to save power. If however there is neither an enable GPIO nor
119deaeeda2SUwe Kleine-König 		 * a regulator keep the PWM on be sure to get a constant
120deaeeda2SUwe Kleine-König 		 * inactive output.
121deaeeda2SUwe Kleine-König 		 */
122deaeeda2SUwe Kleine-König 		state.enabled = !pb->power_supply && !pb->enable_gpio;
123*a10c3d5fSSean Young 		pwm_apply_might_sleep(pb->pwm, &state);
124349ee122SMatthias Kaehlcke 	}
125cc7993f6SDilan Lee 
126cc7993f6SDilan Lee 	if (pb->notify_after)
127cc7993f6SDilan Lee 		pb->notify_after(pb->dev, brightness);
128cc7993f6SDilan Lee 
12942796d37Seric miao 	return 0;
13042796d37Seric miao }
13142796d37Seric miao 
pwm_backlight_check_fb(struct backlight_device * bl,struct fb_info * info)132ef0a5e80SRobert Morell static int pwm_backlight_check_fb(struct backlight_device *bl,
133ef0a5e80SRobert Morell 				  struct fb_info *info)
134ef0a5e80SRobert Morell {
135e6e3dbf9SJingoo Han 	struct pwm_bl_data *pb = bl_get_data(bl);
136ef0a5e80SRobert Morell 
137ef0a5e80SRobert Morell 	return !pb->check_fb || pb->check_fb(pb->dev, info);
138ef0a5e80SRobert Morell }
139ef0a5e80SRobert Morell 
1409905a43bSEmese Revfy static const struct backlight_ops pwm_backlight_ops = {
14142796d37Seric miao 	.update_status	= pwm_backlight_update_status,
142ef0a5e80SRobert Morell 	.check_fb	= pwm_backlight_check_fb,
14342796d37Seric miao };
14442796d37Seric miao 
1453e3ed6cdSThierry Reding #ifdef CONFIG_OF
146ca58b370SRasmus Villemoes #define PWM_LUMINANCE_SHIFT	16
147ca58b370SRasmus Villemoes #define PWM_LUMINANCE_SCALE	(1 << PWM_LUMINANCE_SHIFT) /* luminance scale */
14888ba95beSEnric Balletbo i Serra 
14988ba95beSEnric Balletbo i Serra /*
15088ba95beSEnric Balletbo i Serra  * CIE lightness to PWM conversion.
15188ba95beSEnric Balletbo i Serra  *
15288ba95beSEnric Balletbo i Serra  * The CIE 1931 lightness formula is what actually describes how we perceive
15388ba95beSEnric Balletbo i Serra  * light:
154efdf690eSRasmus Villemoes  *          Y = (L* / 903.3)           if L* ≤ 8
155efdf690eSRasmus Villemoes  *          Y = ((L* + 16) / 116)^3    if L* > 8
15688ba95beSEnric Balletbo i Serra  *
15788ba95beSEnric Balletbo i Serra  * Where Y is the luminance, the amount of light coming out of the screen, and
15888ba95beSEnric Balletbo i Serra  * is a number between 0.0 and 1.0; and L* is the lightness, how bright a human
15988ba95beSEnric Balletbo i Serra  * perceives the screen to be, and is a number between 0 and 100.
16088ba95beSEnric Balletbo i Serra  *
16188ba95beSEnric Balletbo i Serra  * The following function does the fixed point maths needed to implement the
16288ba95beSEnric Balletbo i Serra  * above formula.
16388ba95beSEnric Balletbo i Serra  */
cie1931(unsigned int lightness)164ca58b370SRasmus Villemoes static u64 cie1931(unsigned int lightness)
16588ba95beSEnric Balletbo i Serra {
16688ba95beSEnric Balletbo i Serra 	u64 retval;
16788ba95beSEnric Balletbo i Serra 
168efdf690eSRasmus Villemoes 	/*
169efdf690eSRasmus Villemoes 	 * @lightness is given as a number between 0 and 1, expressed
170ca58b370SRasmus Villemoes 	 * as a fixed-point number in scale
171ca58b370SRasmus Villemoes 	 * PWM_LUMINANCE_SCALE. Convert to a percentage, still
172ca58b370SRasmus Villemoes 	 * expressed as a fixed-point number, so the above formulas
173ca58b370SRasmus Villemoes 	 * can be applied.
174efdf690eSRasmus Villemoes 	 */
17588ba95beSEnric Balletbo i Serra 	lightness *= 100;
176ca58b370SRasmus Villemoes 	if (lightness <= (8 * PWM_LUMINANCE_SCALE)) {
177e802cbafSRasmus Villemoes 		retval = DIV_ROUND_CLOSEST(lightness * 10, 9033);
17888ba95beSEnric Balletbo i Serra 	} else {
179ca58b370SRasmus Villemoes 		retval = (lightness + (16 * PWM_LUMINANCE_SCALE)) / 116;
180407feae1SRasmus Villemoes 		retval *= retval * retval;
181ca58b370SRasmus Villemoes 		retval += 1ULL << (2*PWM_LUMINANCE_SHIFT - 1);
182ca58b370SRasmus Villemoes 		retval >>= 2*PWM_LUMINANCE_SHIFT;
18388ba95beSEnric Balletbo i Serra 	}
18488ba95beSEnric Balletbo i Serra 
18588ba95beSEnric Balletbo i Serra 	return retval;
18688ba95beSEnric Balletbo i Serra }
18788ba95beSEnric Balletbo i Serra 
18888ba95beSEnric Balletbo i Serra /*
18988ba95beSEnric Balletbo i Serra  * Create a default correction table for PWM values to create linear brightness
19088ba95beSEnric Balletbo i Serra  * for LED based backlights using the CIE1931 algorithm.
19188ba95beSEnric Balletbo i Serra  */
19288ba95beSEnric Balletbo i Serra static
pwm_backlight_brightness_default(struct device * dev,struct platform_pwm_backlight_data * data,unsigned int period)19388ba95beSEnric Balletbo i Serra int pwm_backlight_brightness_default(struct device *dev,
19488ba95beSEnric Balletbo i Serra 				     struct platform_pwm_backlight_data *data,
19588ba95beSEnric Balletbo i Serra 				     unsigned int period)
19688ba95beSEnric Balletbo i Serra {
19773fbfc49SMatthias Kaehlcke 	unsigned int i;
19888ba95beSEnric Balletbo i Serra 	u64 retval;
19988ba95beSEnric Balletbo i Serra 
20088ba95beSEnric Balletbo i Serra 	/*
20173fbfc49SMatthias Kaehlcke 	 * Once we have 4096 levels there's little point going much higher...
20273fbfc49SMatthias Kaehlcke 	 * neither interactive sliders nor animation benefits from having
20373fbfc49SMatthias Kaehlcke 	 * more values in the table.
20488ba95beSEnric Balletbo i Serra 	 */
20573fbfc49SMatthias Kaehlcke 	data->max_brightness =
20673fbfc49SMatthias Kaehlcke 		min((int)DIV_ROUND_UP(period, fls(period)), 4096);
20788ba95beSEnric Balletbo i Serra 
20888ba95beSEnric Balletbo i Serra 	data->levels = devm_kcalloc(dev, data->max_brightness,
20988ba95beSEnric Balletbo i Serra 				    sizeof(*data->levels), GFP_KERNEL);
21088ba95beSEnric Balletbo i Serra 	if (!data->levels)
21188ba95beSEnric Balletbo i Serra 		return -ENOMEM;
21288ba95beSEnric Balletbo i Serra 
21388ba95beSEnric Balletbo i Serra 	/* Fill the table using the cie1931 algorithm */
21488ba95beSEnric Balletbo i Serra 	for (i = 0; i < data->max_brightness; i++) {
21588ba95beSEnric Balletbo i Serra 		retval = cie1931((i * PWM_LUMINANCE_SCALE) /
216ca58b370SRasmus Villemoes 				 data->max_brightness) * period;
21788ba95beSEnric Balletbo i Serra 		retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE);
21888ba95beSEnric Balletbo i Serra 		if (retval > UINT_MAX)
21988ba95beSEnric Balletbo i Serra 			return -EINVAL;
22088ba95beSEnric Balletbo i Serra 		data->levels[i] = (unsigned int)retval;
22188ba95beSEnric Balletbo i Serra 	}
22288ba95beSEnric Balletbo i Serra 
22388ba95beSEnric Balletbo i Serra 	data->dft_brightness = data->max_brightness / 2;
22488ba95beSEnric Balletbo i Serra 	data->max_brightness--;
22588ba95beSEnric Balletbo i Serra 
22688ba95beSEnric Balletbo i Serra 	return 0;
22788ba95beSEnric Balletbo i Serra }
22888ba95beSEnric Balletbo i Serra 
pwm_backlight_parse_dt(struct device * dev,struct platform_pwm_backlight_data * data)2293e3ed6cdSThierry Reding static int pwm_backlight_parse_dt(struct device *dev,
2303e3ed6cdSThierry Reding 				  struct platform_pwm_backlight_data *data)
2313e3ed6cdSThierry Reding {
2323e3ed6cdSThierry Reding 	struct device_node *node = dev->of_node;
233789eb04bSAlexandru Stan 	unsigned int num_levels;
23463378673SDaniel Thompson 	unsigned int num_steps = 0;
2353e3ed6cdSThierry Reding 	struct property *prop;
236573fe6d1SEnric Balletbo i Serra 	unsigned int *table;
2373e3ed6cdSThierry Reding 	int length;
2383e3ed6cdSThierry Reding 	u32 value;
2393e3ed6cdSThierry Reding 	int ret;
2403e3ed6cdSThierry Reding 
2413e3ed6cdSThierry Reding 	if (!node)
2423e3ed6cdSThierry Reding 		return -ENODEV;
2433e3ed6cdSThierry Reding 
2443e3ed6cdSThierry Reding 	memset(data, 0, sizeof(*data));
2453e3ed6cdSThierry Reding 
24688ba95beSEnric Balletbo i Serra 	/*
24761170ee9SHeiko Stuebner 	 * These values are optional and set as 0 by default, the out values
24861170ee9SHeiko Stuebner 	 * are modified only if a valid u32 value can be decoded.
24961170ee9SHeiko Stuebner 	 */
25061170ee9SHeiko Stuebner 	of_property_read_u32(node, "post-pwm-on-delay-ms",
25161170ee9SHeiko Stuebner 			     &data->post_pwm_on_delay);
25261170ee9SHeiko Stuebner 	of_property_read_u32(node, "pwm-off-delay-ms", &data->pwm_off_delay);
25361170ee9SHeiko Stuebner 
25461170ee9SHeiko Stuebner 	/*
25588ba95beSEnric Balletbo i Serra 	 * Determine the number of brightness levels, if this property is not
25688ba95beSEnric Balletbo i Serra 	 * set a default table of brightness levels will be used.
25788ba95beSEnric Balletbo i Serra 	 */
2583e3ed6cdSThierry Reding 	prop = of_find_property(node, "brightness-levels", &length);
2593e3ed6cdSThierry Reding 	if (!prop)
26088ba95beSEnric Balletbo i Serra 		return 0;
2613e3ed6cdSThierry Reding 
262789eb04bSAlexandru Stan 	num_levels = length / sizeof(u32);
2633e3ed6cdSThierry Reding 
2643e3ed6cdSThierry Reding 	/* read brightness levels from DT property */
265789eb04bSAlexandru Stan 	if (num_levels > 0) {
266ba9897a0SChristophe JAILLET 		data->levels = devm_kcalloc(dev, num_levels,
267ba9897a0SChristophe JAILLET 					    sizeof(*data->levels), GFP_KERNEL);
2683e3ed6cdSThierry Reding 		if (!data->levels)
2693e3ed6cdSThierry Reding 			return -ENOMEM;
2703e3ed6cdSThierry Reding 
2713e3ed6cdSThierry Reding 		ret = of_property_read_u32_array(node, "brightness-levels",
2723e3ed6cdSThierry Reding 						 data->levels,
273789eb04bSAlexandru Stan 						 num_levels);
2743e3ed6cdSThierry Reding 		if (ret < 0)
2753e3ed6cdSThierry Reding 			return ret;
2763e3ed6cdSThierry Reding 
2773e3ed6cdSThierry Reding 		ret = of_property_read_u32(node, "default-brightness-level",
2783e3ed6cdSThierry Reding 					   &value);
2793e3ed6cdSThierry Reding 		if (ret < 0)
2803e3ed6cdSThierry Reding 			return ret;
2813e3ed6cdSThierry Reding 
2823e3ed6cdSThierry Reding 		data->dft_brightness = value;
283573fe6d1SEnric Balletbo i Serra 
284573fe6d1SEnric Balletbo i Serra 		/*
285573fe6d1SEnric Balletbo i Serra 		 * This property is optional, if is set enables linear
286573fe6d1SEnric Balletbo i Serra 		 * interpolation between each of the values of brightness levels
287573fe6d1SEnric Balletbo i Serra 		 * and creates a new pre-computed table.
288573fe6d1SEnric Balletbo i Serra 		 */
289573fe6d1SEnric Balletbo i Serra 		of_property_read_u32(node, "num-interpolated-steps",
290573fe6d1SEnric Balletbo i Serra 				     &num_steps);
291573fe6d1SEnric Balletbo i Serra 
292573fe6d1SEnric Balletbo i Serra 		/*
293573fe6d1SEnric Balletbo i Serra 		 * Make sure that there is at least two entries in the
294573fe6d1SEnric Balletbo i Serra 		 * brightness-levels table, otherwise we can't interpolate
295573fe6d1SEnric Balletbo i Serra 		 * between two points.
296573fe6d1SEnric Balletbo i Serra 		 */
297573fe6d1SEnric Balletbo i Serra 		if (num_steps) {
298789eb04bSAlexandru Stan 			unsigned int num_input_levels = num_levels;
299789eb04bSAlexandru Stan 			unsigned int i;
300789eb04bSAlexandru Stan 			u32 x1, x2, x, dx;
301789eb04bSAlexandru Stan 			u32 y1, y2;
302789eb04bSAlexandru Stan 			s64 dy;
303789eb04bSAlexandru Stan 
304789eb04bSAlexandru Stan 			if (num_input_levels < 2) {
305573fe6d1SEnric Balletbo i Serra 				dev_err(dev, "can't interpolate\n");
306573fe6d1SEnric Balletbo i Serra 				return -EINVAL;
307573fe6d1SEnric Balletbo i Serra 			}
308573fe6d1SEnric Balletbo i Serra 
309573fe6d1SEnric Balletbo i Serra 			/*
310573fe6d1SEnric Balletbo i Serra 			 * Recalculate the number of brightness levels, now
311573fe6d1SEnric Balletbo i Serra 			 * taking in consideration the number of interpolated
312573fe6d1SEnric Balletbo i Serra 			 * steps between two levels.
313573fe6d1SEnric Balletbo i Serra 			 */
314789eb04bSAlexandru Stan 			num_levels = (num_input_levels - 1) * num_steps + 1;
315573fe6d1SEnric Balletbo i Serra 			dev_dbg(dev, "new number of brightness levels: %d\n",
316573fe6d1SEnric Balletbo i Serra 				num_levels);
317573fe6d1SEnric Balletbo i Serra 
318573fe6d1SEnric Balletbo i Serra 			/*
319573fe6d1SEnric Balletbo i Serra 			 * Create a new table of brightness levels with all the
320573fe6d1SEnric Balletbo i Serra 			 * interpolated steps.
321573fe6d1SEnric Balletbo i Serra 			 */
322ba9897a0SChristophe JAILLET 			table = devm_kcalloc(dev, num_levels, sizeof(*table),
323ba9897a0SChristophe JAILLET 					     GFP_KERNEL);
324573fe6d1SEnric Balletbo i Serra 			if (!table)
325573fe6d1SEnric Balletbo i Serra 				return -ENOMEM;
326789eb04bSAlexandru Stan 			/*
327789eb04bSAlexandru Stan 			 * Fill the interpolated table[x] = y
328789eb04bSAlexandru Stan 			 * by draw lines between each (x1, y1) to (x2, y2).
329789eb04bSAlexandru Stan 			 */
330789eb04bSAlexandru Stan 			dx = num_steps;
331789eb04bSAlexandru Stan 			for (i = 0; i < num_input_levels - 1; i++) {
332789eb04bSAlexandru Stan 				x1 = i * dx;
333789eb04bSAlexandru Stan 				x2 = x1 + dx;
334789eb04bSAlexandru Stan 				y1 = data->levels[i];
335789eb04bSAlexandru Stan 				y2 = data->levels[i + 1];
336789eb04bSAlexandru Stan 				dy = (s64)y2 - y1;
337573fe6d1SEnric Balletbo i Serra 
338789eb04bSAlexandru Stan 				for (x = x1; x < x2; x++) {
339789eb04bSAlexandru Stan 					table[x] = y1 +
340789eb04bSAlexandru Stan 						div_s64(dy * (x - x1), dx);
341573fe6d1SEnric Balletbo i Serra 				}
342573fe6d1SEnric Balletbo i Serra 			}
343789eb04bSAlexandru Stan 			/* Fill in the last point, since no line starts here. */
344789eb04bSAlexandru Stan 			table[x2] = y2;
345573fe6d1SEnric Balletbo i Serra 
346573fe6d1SEnric Balletbo i Serra 			/*
347573fe6d1SEnric Balletbo i Serra 			 * As we use interpolation lets remove current
348573fe6d1SEnric Balletbo i Serra 			 * brightness levels table and replace for the
349573fe6d1SEnric Balletbo i Serra 			 * new interpolated table.
350573fe6d1SEnric Balletbo i Serra 			 */
351573fe6d1SEnric Balletbo i Serra 			devm_kfree(dev, data->levels);
352573fe6d1SEnric Balletbo i Serra 			data->levels = table;
353573fe6d1SEnric Balletbo i Serra 		}
354573fe6d1SEnric Balletbo i Serra 
355789eb04bSAlexandru Stan 		data->max_brightness = num_levels - 1;
3563e3ed6cdSThierry Reding 	}
3573e3ed6cdSThierry Reding 
3583e3ed6cdSThierry Reding 	return 0;
3593e3ed6cdSThierry Reding }
3603e3ed6cdSThierry Reding 
36162cdfe65SArvind Yadav static const struct of_device_id pwm_backlight_of_match[] = {
3623e3ed6cdSThierry Reding 	{ .compatible = "pwm-backlight" },
3633e3ed6cdSThierry Reding 	{ }
3643e3ed6cdSThierry Reding };
3653e3ed6cdSThierry Reding 
3663e3ed6cdSThierry Reding MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
3673e3ed6cdSThierry Reding #else
pwm_backlight_parse_dt(struct device * dev,struct platform_pwm_backlight_data * data)3683e3ed6cdSThierry Reding static int pwm_backlight_parse_dt(struct device *dev,
3693e3ed6cdSThierry Reding 				  struct platform_pwm_backlight_data *data)
3703e3ed6cdSThierry Reding {
3713e3ed6cdSThierry Reding 	return -ENODEV;
3723e3ed6cdSThierry Reding }
37388ba95beSEnric Balletbo i Serra 
37488ba95beSEnric Balletbo i Serra static
pwm_backlight_brightness_default(struct device * dev,struct platform_pwm_backlight_data * data,unsigned int period)37588ba95beSEnric Balletbo i Serra int pwm_backlight_brightness_default(struct device *dev,
37688ba95beSEnric Balletbo i Serra 				     struct platform_pwm_backlight_data *data,
37788ba95beSEnric Balletbo i Serra 				     unsigned int period)
37888ba95beSEnric Balletbo i Serra {
37988ba95beSEnric Balletbo i Serra 	return -ENODEV;
38088ba95beSEnric Balletbo i Serra }
3813e3ed6cdSThierry Reding #endif
3823e3ed6cdSThierry Reding 
pwm_backlight_is_linear(struct platform_pwm_backlight_data * data)383c0b64fafSMatthias Kaehlcke static bool pwm_backlight_is_linear(struct platform_pwm_backlight_data *data)
384c0b64fafSMatthias Kaehlcke {
385c0b64fafSMatthias Kaehlcke 	unsigned int nlevels = data->max_brightness + 1;
386c0b64fafSMatthias Kaehlcke 	unsigned int min_val = data->levels[0];
387c0b64fafSMatthias Kaehlcke 	unsigned int max_val = data->levels[nlevels - 1];
388c0b64fafSMatthias Kaehlcke 	/*
389c0b64fafSMatthias Kaehlcke 	 * Multiplying by 128 means that even in pathological cases such
390c0b64fafSMatthias Kaehlcke 	 * as (max_val - min_val) == nlevels the error at max_val is less
391c0b64fafSMatthias Kaehlcke 	 * than 1%.
392c0b64fafSMatthias Kaehlcke 	 */
393c0b64fafSMatthias Kaehlcke 	unsigned int slope = (128 * (max_val - min_val)) / nlevels;
394c0b64fafSMatthias Kaehlcke 	unsigned int margin = (max_val - min_val) / 20; /* 5% */
395c0b64fafSMatthias Kaehlcke 	int i;
396c0b64fafSMatthias Kaehlcke 
397c0b64fafSMatthias Kaehlcke 	for (i = 1; i < nlevels; i++) {
398c0b64fafSMatthias Kaehlcke 		unsigned int linear_value = min_val + ((i * slope) / 128);
399c0b64fafSMatthias Kaehlcke 		unsigned int delta = abs(linear_value - data->levels[i]);
400c0b64fafSMatthias Kaehlcke 
401c0b64fafSMatthias Kaehlcke 		if (delta > margin)
402c0b64fafSMatthias Kaehlcke 			return false;
403c0b64fafSMatthias Kaehlcke 	}
404c0b64fafSMatthias Kaehlcke 
405c0b64fafSMatthias Kaehlcke 	return true;
406c0b64fafSMatthias Kaehlcke }
407c0b64fafSMatthias Kaehlcke 
pwm_backlight_initial_power_state(const struct pwm_bl_data * pb)4087613c922SPeter Ujfalusi static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
4097613c922SPeter Ujfalusi {
4107613c922SPeter Ujfalusi 	struct device_node *node = pb->dev->of_node;
41179fad92fSDaniel Thompson 	bool active = true;
41279fad92fSDaniel Thompson 
41379fad92fSDaniel Thompson 	/*
41479fad92fSDaniel Thompson 	 * If the enable GPIO is present, observable (either as input
41579fad92fSDaniel Thompson 	 * or output) and off then the backlight is not currently active.
41679fad92fSDaniel Thompson 	 * */
41779fad92fSDaniel Thompson 	if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0)
41879fad92fSDaniel Thompson 		active = false;
41979fad92fSDaniel Thompson 
420deaeeda2SUwe Kleine-König 	if (pb->power_supply && !regulator_is_enabled(pb->power_supply))
42179fad92fSDaniel Thompson 		active = false;
42279fad92fSDaniel Thompson 
42379fad92fSDaniel Thompson 	if (!pwm_is_enabled(pb->pwm))
42479fad92fSDaniel Thompson 		active = false;
42579fad92fSDaniel Thompson 
42679fad92fSDaniel Thompson 	/*
42779fad92fSDaniel Thompson 	 * Synchronize the enable_gpio with the observed state of the
42879fad92fSDaniel Thompson 	 * hardware.
42979fad92fSDaniel Thompson 	 */
43079fad92fSDaniel Thompson 	gpiod_direction_output(pb->enable_gpio, active);
43179fad92fSDaniel Thompson 
43279fad92fSDaniel Thompson 	/*
43379fad92fSDaniel Thompson 	 * Do not change pb->enabled here! pb->enabled essentially
43479fad92fSDaniel Thompson 	 * tells us if we own one of the regulator's use counts and
43579fad92fSDaniel Thompson 	 * right now we do not.
43679fad92fSDaniel Thompson 	 */
4377613c922SPeter Ujfalusi 
4387613c922SPeter Ujfalusi 	/* Not booted with device tree or no phandle link to the node */
4397613c922SPeter Ujfalusi 	if (!node || !node->phandle)
4407613c922SPeter Ujfalusi 		return FB_BLANK_UNBLANK;
4417613c922SPeter Ujfalusi 
4427613c922SPeter Ujfalusi 	/*
4437613c922SPeter Ujfalusi 	 * If the driver is probed from the device tree and there is a
4447613c922SPeter Ujfalusi 	 * phandle link pointing to the backlight node, it is safe to
4457613c922SPeter Ujfalusi 	 * assume that another driver will enable the backlight at the
4467613c922SPeter Ujfalusi 	 * appropriate time. Therefore, if it is disabled, keep it so.
4477613c922SPeter Ujfalusi 	 */
44879fad92fSDaniel Thompson 	return active ? FB_BLANK_UNBLANK: FB_BLANK_POWERDOWN;
4497613c922SPeter Ujfalusi }
4507613c922SPeter Ujfalusi 
pwm_backlight_probe(struct platform_device * pdev)45142796d37Seric miao static int pwm_backlight_probe(struct platform_device *pdev)
45242796d37Seric miao {
453c512794cSJingoo Han 	struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
4543e3ed6cdSThierry Reding 	struct platform_pwm_backlight_data defdata;
4553e3ed6cdSThierry Reding 	struct backlight_properties props;
45642796d37Seric miao 	struct backlight_device *bl;
45742796d37Seric miao 	struct pwm_bl_data *pb;
45888ba95beSEnric Balletbo i Serra 	struct pwm_state state;
45988ba95beSEnric Balletbo i Serra 	unsigned int i;
4603b73125aSPhilipp Zabel 	int ret;
46142796d37Seric miao 
46214563a4eSBen Dooks 	if (!data) {
4633e3ed6cdSThierry Reding 		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
4643e3ed6cdSThierry Reding 		if (ret < 0) {
46514563a4eSBen Dooks 			dev_err(&pdev->dev, "failed to find platform data\n");
4663e3ed6cdSThierry Reding 			return ret;
4673e3ed6cdSThierry Reding 		}
4683e3ed6cdSThierry Reding 
4693e3ed6cdSThierry Reding 		data = &defdata;
47014563a4eSBen Dooks 	}
47142796d37Seric miao 
4723b73125aSPhilipp Zabel 	if (data->init) {
4733b73125aSPhilipp Zabel 		ret = data->init(&pdev->dev);
4743b73125aSPhilipp Zabel 		if (ret < 0)
4753b73125aSPhilipp Zabel 			return ret;
4763b73125aSPhilipp Zabel 	}
4773b73125aSPhilipp Zabel 
478ce969228SJulia Lawall 	pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
4793b73125aSPhilipp Zabel 	if (!pb) {
4803b73125aSPhilipp Zabel 		ret = -ENOMEM;
4813b73125aSPhilipp Zabel 		goto err_alloc;
4823b73125aSPhilipp Zabel 	}
48342796d37Seric miao 
4843b73125aSPhilipp Zabel 	pb->notify = data->notify;
485cc7993f6SDilan Lee 	pb->notify_after = data->notify_after;
486ef0a5e80SRobert Morell 	pb->check_fb = data->check_fb;
4873e3ed6cdSThierry Reding 	pb->exit = data->exit;
488cfc3899fSBen Dooks 	pb->dev = &pdev->dev;
489e4c8ae3eSHeiko Stuebner 	pb->enabled = false;
4903157694dSEnric Balletbo i Serra 	pb->post_pwm_on_delay = data->post_pwm_on_delay;
4913157694dSEnric Balletbo i Serra 	pb->pwm_off_delay = data->pwm_off_delay;
49242796d37Seric miao 
493cdaefcceSAxel Lin 	pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
4943698d7e7SPhilipp Zabel 						  GPIOD_ASIS);
495257462dbSAlexandre Courbot 	if (IS_ERR(pb->enable_gpio)) {
496257462dbSAlexandre Courbot 		ret = PTR_ERR(pb->enable_gpio);
4978265b2e4SThierry Reding 		goto err_alloc;
4988265b2e4SThierry Reding 	}
499257462dbSAlexandre Courbot 
500deaeeda2SUwe Kleine-König 	pb->power_supply = devm_regulator_get_optional(&pdev->dev, "power");
50122ceeee1SThierry Reding 	if (IS_ERR(pb->power_supply)) {
50222ceeee1SThierry Reding 		ret = PTR_ERR(pb->power_supply);
503deaeeda2SUwe Kleine-König 		if (ret == -ENODEV)
504deaeeda2SUwe Kleine-König 			pb->power_supply = NULL;
505deaeeda2SUwe Kleine-König 		else
506257462dbSAlexandre Courbot 			goto err_alloc;
50722ceeee1SThierry Reding 	}
50842796d37Seric miao 
50960ce7028SSachin Kamat 	pb->pwm = devm_pwm_get(&pdev->dev, NULL);
510dc881123SVladimir Zapolskiy 	if (IS_ERR(pb->pwm)) {
511dc881123SVladimir Zapolskiy 		ret = PTR_ERR(pb->pwm);
512dc881123SVladimir Zapolskiy 		if (ret != -EPROBE_DEFER)
513dc881123SVladimir Zapolskiy 			dev_err(&pdev->dev, "unable to request PWM\n");
514dc881123SVladimir Zapolskiy 		goto err_alloc;
5153e3ed6cdSThierry Reding 	}
5163e3ed6cdSThierry Reding 
51714563a4eSBen Dooks 	dev_dbg(&pdev->dev, "got pwm for backlight\n");
51842796d37Seric miao 
519e6bcca08SEnric Balletbo i Serra 	/* Sync up PWM state. */
520e6bcca08SEnric Balletbo i Serra 	pwm_init_state(pb->pwm, &state);
52188ba95beSEnric Balletbo i Serra 
522e6bcca08SEnric Balletbo i Serra 	/*
523e6bcca08SEnric Balletbo i Serra 	 * The DT case will set the pwm_period_ns field to 0 and store the
524e6bcca08SEnric Balletbo i Serra 	 * period, parsed from the DT, in the PWM device. For the non-DT case,
525e6bcca08SEnric Balletbo i Serra 	 * set the period from platform data if it has not already been set
526e6bcca08SEnric Balletbo i Serra 	 * via the PWM lookup table.
527e6bcca08SEnric Balletbo i Serra 	 */
528e6bcca08SEnric Balletbo i Serra 	if (!state.period && (data->pwm_period_ns > 0))
529e6bcca08SEnric Balletbo i Serra 		state.period = data->pwm_period_ns;
530e6bcca08SEnric Balletbo i Serra 
531*a10c3d5fSSean Young 	ret = pwm_apply_might_sleep(pb->pwm, &state);
532e6bcca08SEnric Balletbo i Serra 	if (ret) {
533e6bcca08SEnric Balletbo i Serra 		dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n",
534e6bcca08SEnric Balletbo i Serra 			ret);
535e6bcca08SEnric Balletbo i Serra 		goto err_alloc;
536e6bcca08SEnric Balletbo i Serra 	}
537e6bcca08SEnric Balletbo i Serra 
538511a2046SMatthias Kaehlcke 	memset(&props, 0, sizeof(struct backlight_properties));
539511a2046SMatthias Kaehlcke 
540d347d0c8SEnric Balletbo i Serra 	if (data->levels) {
541de6f2a7fSMatthias Kaehlcke 		pb->levels = data->levels;
542de6f2a7fSMatthias Kaehlcke 
543d347d0c8SEnric Balletbo i Serra 		/*
544d347d0c8SEnric Balletbo i Serra 		 * For the DT case, only when brightness levels is defined
545d347d0c8SEnric Balletbo i Serra 		 * data->levels is filled. For the non-DT case, data->levels
546d347d0c8SEnric Balletbo i Serra 		 * can come from platform data, however is not usual.
547d347d0c8SEnric Balletbo i Serra 		 */
548de6f2a7fSMatthias Kaehlcke 		for (i = 0; i <= data->max_brightness; i++)
549d347d0c8SEnric Balletbo i Serra 			if (data->levels[i] > pb->scale)
550d347d0c8SEnric Balletbo i Serra 				pb->scale = data->levels[i];
551d347d0c8SEnric Balletbo i Serra 
552c0b64fafSMatthias Kaehlcke 		if (pwm_backlight_is_linear(data))
553c0b64fafSMatthias Kaehlcke 			props.scale = BACKLIGHT_SCALE_LINEAR;
554c0b64fafSMatthias Kaehlcke 		else
555c0b64fafSMatthias Kaehlcke 			props.scale = BACKLIGHT_SCALE_NON_LINEAR;
556d347d0c8SEnric Balletbo i Serra 	} else if (!data->max_brightness) {
557d347d0c8SEnric Balletbo i Serra 		/*
558d347d0c8SEnric Balletbo i Serra 		 * If no brightness levels are provided and max_brightness is
559d347d0c8SEnric Balletbo i Serra 		 * not set, use the default brightness table. For the DT case,
560d347d0c8SEnric Balletbo i Serra 		 * max_brightness is set to 0 when brightness levels is not
561d347d0c8SEnric Balletbo i Serra 		 * specified. For the non-DT case, max_brightness is usually
562d347d0c8SEnric Balletbo i Serra 		 * set to some value.
563d347d0c8SEnric Balletbo i Serra 		 */
564d347d0c8SEnric Balletbo i Serra 
565d347d0c8SEnric Balletbo i Serra 		/* Get the PWM period (in nanoseconds) */
566d347d0c8SEnric Balletbo i Serra 		pwm_get_state(pb->pwm, &state);
567d347d0c8SEnric Balletbo i Serra 
56888ba95beSEnric Balletbo i Serra 		ret = pwm_backlight_brightness_default(&pdev->dev, data,
56988ba95beSEnric Balletbo i Serra 						       state.period);
57088ba95beSEnric Balletbo i Serra 		if (ret < 0) {
57188ba95beSEnric Balletbo i Serra 			dev_err(&pdev->dev,
57288ba95beSEnric Balletbo i Serra 				"failed to setup default brightness table\n");
57388ba95beSEnric Balletbo i Serra 			goto err_alloc;
57488ba95beSEnric Balletbo i Serra 		}
57588ba95beSEnric Balletbo i Serra 
57688ba95beSEnric Balletbo i Serra 		for (i = 0; i <= data->max_brightness; i++) {
57788ba95beSEnric Balletbo i Serra 			if (data->levels[i] > pb->scale)
57888ba95beSEnric Balletbo i Serra 				pb->scale = data->levels[i];
57988ba95beSEnric Balletbo i Serra 
58088ba95beSEnric Balletbo i Serra 			pb->levels = data->levels;
58188ba95beSEnric Balletbo i Serra 		}
582511a2046SMatthias Kaehlcke 
583511a2046SMatthias Kaehlcke 		props.scale = BACKLIGHT_SCALE_NON_LINEAR;
584d347d0c8SEnric Balletbo i Serra 	} else {
585d347d0c8SEnric Balletbo i Serra 		/*
586d347d0c8SEnric Balletbo i Serra 		 * That only happens for the non-DT case, where platform data
587d347d0c8SEnric Balletbo i Serra 		 * sets the max_brightness value.
588d347d0c8SEnric Balletbo i Serra 		 */
589d347d0c8SEnric Balletbo i Serra 		pb->scale = data->max_brightness;
590d347d0c8SEnric Balletbo i Serra 	}
59188ba95beSEnric Balletbo i Serra 
592134ada17SGuru Das Srinagesh 	pb->lth_brightness = data->lth_brightness * (div_u64(state.period,
593134ada17SGuru Das Srinagesh 				pb->scale));
5943e3ed6cdSThierry Reding 
595bb7ca747SMatthew Garrett 	props.type = BACKLIGHT_RAW;
596a19a6ee6SMatthew Garrett 	props.max_brightness = data->max_brightness;
597a19a6ee6SMatthew Garrett 	bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
598a19a6ee6SMatthew Garrett 				       &pwm_backlight_ops, &props);
59942796d37Seric miao 	if (IS_ERR(bl)) {
60042796d37Seric miao 		dev_err(&pdev->dev, "failed to register backlight\n");
6013b73125aSPhilipp Zabel 		ret = PTR_ERR(bl);
602257462dbSAlexandre Courbot 		goto err_alloc;
60342796d37Seric miao 	}
60442796d37Seric miao 
60583cfd726SPeter Ujfalusi 	if (data->dft_brightness > data->max_brightness) {
60683cfd726SPeter Ujfalusi 		dev_warn(&pdev->dev,
60783cfd726SPeter Ujfalusi 			 "invalid default brightness level: %u, using %u\n",
60883cfd726SPeter Ujfalusi 			 data->dft_brightness, data->max_brightness);
60983cfd726SPeter Ujfalusi 		data->dft_brightness = data->max_brightness;
61083cfd726SPeter Ujfalusi 	}
61183cfd726SPeter Ujfalusi 
61242796d37Seric miao 	bl->props.brightness = data->dft_brightness;
6137613c922SPeter Ujfalusi 	bl->props.power = pwm_backlight_initial_power_state(pb);
61442796d37Seric miao 	backlight_update_status(bl);
61542796d37Seric miao 
61642796d37Seric miao 	platform_set_drvdata(pdev, bl);
61742796d37Seric miao 	return 0;
6183b73125aSPhilipp Zabel 
6193b73125aSPhilipp Zabel err_alloc:
6203b73125aSPhilipp Zabel 	if (data->exit)
6213b73125aSPhilipp Zabel 		data->exit(&pdev->dev);
6223b73125aSPhilipp Zabel 	return ret;
62342796d37Seric miao }
62442796d37Seric miao 
pwm_backlight_remove(struct platform_device * pdev)6250a4606a7SUwe Kleine-König static void pwm_backlight_remove(struct platform_device *pdev)
62642796d37Seric miao {
62742796d37Seric miao 	struct backlight_device *bl = platform_get_drvdata(pdev);
628e6e3dbf9SJingoo Han 	struct pwm_bl_data *pb = bl_get_data(bl);
62980d5b1bfSUwe Kleine-König 	struct pwm_state state;
63042796d37Seric miao 
63142796d37Seric miao 	backlight_device_unregister(bl);
63262b744a8SThierry Reding 	pwm_backlight_power_off(pb);
63380d5b1bfSUwe Kleine-König 	pwm_get_state(pb->pwm, &state);
63480d5b1bfSUwe Kleine-König 	state.duty_cycle = 0;
63580d5b1bfSUwe Kleine-König 	state.enabled = false;
636*a10c3d5fSSean Young 	pwm_apply_might_sleep(pb->pwm, &state);
637668e63c6SThierry Reding 
6383e3ed6cdSThierry Reding 	if (pb->exit)
6393e3ed6cdSThierry Reding 		pb->exit(&pdev->dev);
64042796d37Seric miao }
64142796d37Seric miao 
pwm_backlight_shutdown(struct platform_device * pdev)6425f33b896SThierry Reding static void pwm_backlight_shutdown(struct platform_device *pdev)
6435f33b896SThierry Reding {
6445f33b896SThierry Reding 	struct backlight_device *bl = platform_get_drvdata(pdev);
6455f33b896SThierry Reding 	struct pwm_bl_data *pb = bl_get_data(bl);
64680d5b1bfSUwe Kleine-König 	struct pwm_state state;
6475f33b896SThierry Reding 
6485f33b896SThierry Reding 	pwm_backlight_power_off(pb);
64980d5b1bfSUwe Kleine-König 	pwm_get_state(pb->pwm, &state);
65080d5b1bfSUwe Kleine-König 	state.duty_cycle = 0;
65180d5b1bfSUwe Kleine-König 	state.enabled = false;
652*a10c3d5fSSean Young 	pwm_apply_might_sleep(pb->pwm, &state);
6535f33b896SThierry Reding }
6545f33b896SThierry Reding 
655c791126bSJingoo Han #ifdef CONFIG_PM_SLEEP
pwm_backlight_suspend(struct device * dev)656e2c17bc6SMark Brown static int pwm_backlight_suspend(struct device *dev)
65742796d37Seric miao {
658e2c17bc6SMark Brown 	struct backlight_device *bl = dev_get_drvdata(dev);
659e6e3dbf9SJingoo Han 	struct pwm_bl_data *pb = bl_get_data(bl);
66080d5b1bfSUwe Kleine-König 	struct pwm_state state;
66142796d37Seric miao 
66282e8b542SMarc Zyngier 	if (pb->notify)
663cfc3899fSBen Dooks 		pb->notify(pb->dev, 0);
664668e63c6SThierry Reding 
66562b744a8SThierry Reding 	pwm_backlight_power_off(pb);
666668e63c6SThierry Reding 
66780d5b1bfSUwe Kleine-König 	/*
66880d5b1bfSUwe Kleine-König 	 * Note that disabling the PWM doesn't guarantee that the output stays
66980d5b1bfSUwe Kleine-König 	 * at its inactive state. However without the PWM disabled, the PWM
67080d5b1bfSUwe Kleine-König 	 * driver refuses to suspend. So disable here even though this might
67180d5b1bfSUwe Kleine-König 	 * enable the backlight on poorly designed boards.
67280d5b1bfSUwe Kleine-König 	 */
67380d5b1bfSUwe Kleine-König 	pwm_get_state(pb->pwm, &state);
67480d5b1bfSUwe Kleine-König 	state.duty_cycle = 0;
67580d5b1bfSUwe Kleine-König 	state.enabled = false;
676*a10c3d5fSSean Young 	pwm_apply_might_sleep(pb->pwm, &state);
67780d5b1bfSUwe Kleine-König 
678cc7993f6SDilan Lee 	if (pb->notify_after)
679cc7993f6SDilan Lee 		pb->notify_after(pb->dev, 0);
680668e63c6SThierry Reding 
68142796d37Seric miao 	return 0;
68242796d37Seric miao }
68342796d37Seric miao 
pwm_backlight_resume(struct device * dev)684e2c17bc6SMark Brown static int pwm_backlight_resume(struct device *dev)
68542796d37Seric miao {
686e2c17bc6SMark Brown 	struct backlight_device *bl = dev_get_drvdata(dev);
68742796d37Seric miao 
68842796d37Seric miao 	backlight_update_status(bl);
689668e63c6SThierry Reding 
69042796d37Seric miao 	return 0;
69142796d37Seric miao }
692c791126bSJingoo Han #endif
693e2c17bc6SMark Brown 
6941dea1fd0SHuayi Li static const struct dev_pm_ops pwm_backlight_pm_ops = {
6951dea1fd0SHuayi Li #ifdef CONFIG_PM_SLEEP
6961dea1fd0SHuayi Li 	.suspend = pwm_backlight_suspend,
6971dea1fd0SHuayi Li 	.resume = pwm_backlight_resume,
6981dea1fd0SHuayi Li 	.poweroff = pwm_backlight_suspend,
6991dea1fd0SHuayi Li 	.restore = pwm_backlight_resume,
7001dea1fd0SHuayi Li #endif
7011dea1fd0SHuayi Li };
702e2c17bc6SMark Brown 
70342796d37Seric miao static struct platform_driver pwm_backlight_driver = {
70442796d37Seric miao 	.driver		= {
70542796d37Seric miao 		.name		= "pwm-backlight",
706e2c17bc6SMark Brown 		.pm		= &pwm_backlight_pm_ops,
7073e3ed6cdSThierry Reding 		.of_match_table	= of_match_ptr(pwm_backlight_of_match),
70842796d37Seric miao 	},
70942796d37Seric miao 	.probe		= pwm_backlight_probe,
7100a4606a7SUwe Kleine-König 	.remove_new	= pwm_backlight_remove,
7115f33b896SThierry Reding 	.shutdown	= pwm_backlight_shutdown,
71242796d37Seric miao };
71342796d37Seric miao 
71481178e02SAxel Lin module_platform_driver(pwm_backlight_driver);
71542796d37Seric miao 
71642796d37Seric miao MODULE_DESCRIPTION("PWM based Backlight Driver");
7175076fbedSAndy Shevchenko MODULE_LICENSE("GPL v2");
7188cd68198SBen Dooks MODULE_ALIAS("platform:pwm-backlight");
719