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