1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Simple PWM based backlight control, board code has to setup |
4 | * 1) pin configuration so PWM waveforms can output |
5 | * 2) platform_data being correctly configured |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/gpio/consumer.h> |
10 | #include <linux/module.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/init.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/fb.h> |
15 | #include <linux/backlight.h> |
16 | #include <linux/err.h> |
17 | #include <linux/pwm.h> |
18 | #include <linux/pwm_backlight.h> |
19 | #include <linux/regulator/consumer.h> |
20 | #include <linux/slab.h> |
21 | |
22 | struct pwm_bl_data { |
23 | struct pwm_device *pwm; |
24 | struct device *dev; |
25 | unsigned int lth_brightness; |
26 | unsigned int *levels; |
27 | bool enabled; |
28 | struct regulator *power_supply; |
29 | struct gpio_desc *enable_gpio; |
30 | unsigned int scale; |
31 | unsigned int post_pwm_on_delay; |
32 | unsigned int pwm_off_delay; |
33 | int (*notify)(struct device *, |
34 | int brightness); |
35 | void (*notify_after)(struct device *, |
36 | int brightness); |
37 | int (*check_fb)(struct device *, struct fb_info *); |
38 | void (*exit)(struct device *); |
39 | }; |
40 | |
41 | static void pwm_backlight_power_on(struct pwm_bl_data *pb) |
42 | { |
43 | int err; |
44 | |
45 | if (pb->enabled) |
46 | return; |
47 | |
48 | if (pb->power_supply) { |
49 | err = regulator_enable(regulator: pb->power_supply); |
50 | if (err < 0) |
51 | dev_err(pb->dev, "failed to enable power supply\n" ); |
52 | } |
53 | |
54 | if (pb->post_pwm_on_delay) |
55 | msleep(msecs: pb->post_pwm_on_delay); |
56 | |
57 | gpiod_set_value_cansleep(desc: pb->enable_gpio, value: 1); |
58 | |
59 | pb->enabled = true; |
60 | } |
61 | |
62 | static void pwm_backlight_power_off(struct pwm_bl_data *pb) |
63 | { |
64 | if (!pb->enabled) |
65 | return; |
66 | |
67 | gpiod_set_value_cansleep(desc: pb->enable_gpio, value: 0); |
68 | |
69 | if (pb->pwm_off_delay) |
70 | msleep(msecs: pb->pwm_off_delay); |
71 | |
72 | if (pb->power_supply) |
73 | regulator_disable(regulator: pb->power_supply); |
74 | pb->enabled = false; |
75 | } |
76 | |
77 | static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness, struct pwm_state *state) |
78 | { |
79 | unsigned int lth = pb->lth_brightness; |
80 | u64 duty_cycle; |
81 | |
82 | if (pb->levels) |
83 | duty_cycle = pb->levels[brightness]; |
84 | else |
85 | duty_cycle = brightness; |
86 | |
87 | duty_cycle *= state->period - lth; |
88 | do_div(duty_cycle, pb->scale); |
89 | |
90 | return duty_cycle + lth; |
91 | } |
92 | |
93 | static int pwm_backlight_update_status(struct backlight_device *bl) |
94 | { |
95 | struct pwm_bl_data *pb = bl_get_data(bl_dev: bl); |
96 | int brightness = backlight_get_brightness(bd: bl); |
97 | struct pwm_state state; |
98 | |
99 | if (pb->notify) |
100 | brightness = pb->notify(pb->dev, brightness); |
101 | |
102 | if (brightness > 0) { |
103 | pwm_get_state(pwm: pb->pwm, state: &state); |
104 | state.duty_cycle = compute_duty_cycle(pb, brightness, state: &state); |
105 | state.enabled = true; |
106 | pwm_apply_state(pwm: pb->pwm, state: &state); |
107 | |
108 | pwm_backlight_power_on(pb); |
109 | } else { |
110 | pwm_backlight_power_off(pb); |
111 | |
112 | pwm_get_state(pwm: pb->pwm, state: &state); |
113 | state.duty_cycle = 0; |
114 | /* |
115 | * We cannot assume a disabled PWM to drive its output to the |
116 | * inactive state. If we have an enable GPIO and/or a regulator |
117 | * we assume that this isn't relevant and we can disable the PWM |
118 | * to save power. If however there is neither an enable GPIO nor |
119 | * a regulator keep the PWM on be sure to get a constant |
120 | * inactive output. |
121 | */ |
122 | state.enabled = !pb->power_supply && !pb->enable_gpio; |
123 | pwm_apply_state(pwm: pb->pwm, state: &state); |
124 | } |
125 | |
126 | if (pb->notify_after) |
127 | pb->notify_after(pb->dev, brightness); |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static int pwm_backlight_check_fb(struct backlight_device *bl, |
133 | struct fb_info *info) |
134 | { |
135 | struct pwm_bl_data *pb = bl_get_data(bl_dev: bl); |
136 | |
137 | return !pb->check_fb || pb->check_fb(pb->dev, info); |
138 | } |
139 | |
140 | static const struct backlight_ops pwm_backlight_ops = { |
141 | .update_status = pwm_backlight_update_status, |
142 | .check_fb = pwm_backlight_check_fb, |
143 | }; |
144 | |
145 | #ifdef CONFIG_OF |
146 | #define PWM_LUMINANCE_SHIFT 16 |
147 | #define PWM_LUMINANCE_SCALE (1 << PWM_LUMINANCE_SHIFT) /* luminance scale */ |
148 | |
149 | /* |
150 | * CIE lightness to PWM conversion. |
151 | * |
152 | * The CIE 1931 lightness formula is what actually describes how we perceive |
153 | * light: |
154 | * Y = (L* / 903.3) if L* ≤ 8 |
155 | * Y = ((L* + 16) / 116)^3 if L* > 8 |
156 | * |
157 | * Where Y is the luminance, the amount of light coming out of the screen, and |
158 | * is a number between 0.0 and 1.0; and L* is the lightness, how bright a human |
159 | * perceives the screen to be, and is a number between 0 and 100. |
160 | * |
161 | * The following function does the fixed point maths needed to implement the |
162 | * above formula. |
163 | */ |
164 | static u64 cie1931(unsigned int lightness) |
165 | { |
166 | u64 retval; |
167 | |
168 | /* |
169 | * @lightness is given as a number between 0 and 1, expressed |
170 | * as a fixed-point number in scale |
171 | * PWM_LUMINANCE_SCALE. Convert to a percentage, still |
172 | * expressed as a fixed-point number, so the above formulas |
173 | * can be applied. |
174 | */ |
175 | lightness *= 100; |
176 | if (lightness <= (8 * PWM_LUMINANCE_SCALE)) { |
177 | retval = DIV_ROUND_CLOSEST(lightness * 10, 9033); |
178 | } else { |
179 | retval = (lightness + (16 * PWM_LUMINANCE_SCALE)) / 116; |
180 | retval *= retval * retval; |
181 | retval += 1ULL << (2*PWM_LUMINANCE_SHIFT - 1); |
182 | retval >>= 2*PWM_LUMINANCE_SHIFT; |
183 | } |
184 | |
185 | return retval; |
186 | } |
187 | |
188 | /* |
189 | * Create a default correction table for PWM values to create linear brightness |
190 | * for LED based backlights using the CIE1931 algorithm. |
191 | */ |
192 | static |
193 | int pwm_backlight_brightness_default(struct device *dev, |
194 | struct platform_pwm_backlight_data *data, |
195 | unsigned int period) |
196 | { |
197 | unsigned int i; |
198 | u64 retval; |
199 | |
200 | /* |
201 | * Once we have 4096 levels there's little point going much higher... |
202 | * neither interactive sliders nor animation benefits from having |
203 | * more values in the table. |
204 | */ |
205 | data->max_brightness = |
206 | min((int)DIV_ROUND_UP(period, fls(period)), 4096); |
207 | |
208 | data->levels = devm_kcalloc(dev, n: data->max_brightness, |
209 | size: sizeof(*data->levels), GFP_KERNEL); |
210 | if (!data->levels) |
211 | return -ENOMEM; |
212 | |
213 | /* Fill the table using the cie1931 algorithm */ |
214 | for (i = 0; i < data->max_brightness; i++) { |
215 | retval = cie1931(lightness: (i * PWM_LUMINANCE_SCALE) / |
216 | data->max_brightness) * period; |
217 | retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE); |
218 | if (retval > UINT_MAX) |
219 | return -EINVAL; |
220 | data->levels[i] = (unsigned int)retval; |
221 | } |
222 | |
223 | data->dft_brightness = data->max_brightness / 2; |
224 | data->max_brightness--; |
225 | |
226 | return 0; |
227 | } |
228 | |
229 | static int pwm_backlight_parse_dt(struct device *dev, |
230 | struct platform_pwm_backlight_data *data) |
231 | { |
232 | struct device_node *node = dev->of_node; |
233 | unsigned int num_levels; |
234 | unsigned int num_steps = 0; |
235 | struct property *prop; |
236 | unsigned int *table; |
237 | int length; |
238 | u32 value; |
239 | int ret; |
240 | |
241 | if (!node) |
242 | return -ENODEV; |
243 | |
244 | memset(data, 0, sizeof(*data)); |
245 | |
246 | /* |
247 | * These values are optional and set as 0 by default, the out values |
248 | * are modified only if a valid u32 value can be decoded. |
249 | */ |
250 | of_property_read_u32(np: node, propname: "post-pwm-on-delay-ms" , |
251 | out_value: &data->post_pwm_on_delay); |
252 | of_property_read_u32(np: node, propname: "pwm-off-delay-ms" , out_value: &data->pwm_off_delay); |
253 | |
254 | /* |
255 | * Determine the number of brightness levels, if this property is not |
256 | * set a default table of brightness levels will be used. |
257 | */ |
258 | prop = of_find_property(np: node, name: "brightness-levels" , lenp: &length); |
259 | if (!prop) |
260 | return 0; |
261 | |
262 | num_levels = length / sizeof(u32); |
263 | |
264 | /* read brightness levels from DT property */ |
265 | if (num_levels > 0) { |
266 | data->levels = devm_kcalloc(dev, n: num_levels, |
267 | size: sizeof(*data->levels), GFP_KERNEL); |
268 | if (!data->levels) |
269 | return -ENOMEM; |
270 | |
271 | ret = of_property_read_u32_array(np: node, propname: "brightness-levels" , |
272 | out_values: data->levels, |
273 | sz: num_levels); |
274 | if (ret < 0) |
275 | return ret; |
276 | |
277 | ret = of_property_read_u32(np: node, propname: "default-brightness-level" , |
278 | out_value: &value); |
279 | if (ret < 0) |
280 | return ret; |
281 | |
282 | data->dft_brightness = value; |
283 | |
284 | /* |
285 | * This property is optional, if is set enables linear |
286 | * interpolation between each of the values of brightness levels |
287 | * and creates a new pre-computed table. |
288 | */ |
289 | of_property_read_u32(np: node, propname: "num-interpolated-steps" , |
290 | out_value: &num_steps); |
291 | |
292 | /* |
293 | * Make sure that there is at least two entries in the |
294 | * brightness-levels table, otherwise we can't interpolate |
295 | * between two points. |
296 | */ |
297 | if (num_steps) { |
298 | unsigned int num_input_levels = num_levels; |
299 | unsigned int i; |
300 | u32 x1, x2, x, dx; |
301 | u32 y1, y2; |
302 | s64 dy; |
303 | |
304 | if (num_input_levels < 2) { |
305 | dev_err(dev, "can't interpolate\n" ); |
306 | return -EINVAL; |
307 | } |
308 | |
309 | /* |
310 | * Recalculate the number of brightness levels, now |
311 | * taking in consideration the number of interpolated |
312 | * steps between two levels. |
313 | */ |
314 | num_levels = (num_input_levels - 1) * num_steps + 1; |
315 | dev_dbg(dev, "new number of brightness levels: %d\n" , |
316 | num_levels); |
317 | |
318 | /* |
319 | * Create a new table of brightness levels with all the |
320 | * interpolated steps. |
321 | */ |
322 | table = devm_kcalloc(dev, n: num_levels, size: sizeof(*table), |
323 | GFP_KERNEL); |
324 | if (!table) |
325 | return -ENOMEM; |
326 | /* |
327 | * Fill the interpolated table[x] = y |
328 | * by draw lines between each (x1, y1) to (x2, y2). |
329 | */ |
330 | dx = num_steps; |
331 | for (i = 0; i < num_input_levels - 1; i++) { |
332 | x1 = i * dx; |
333 | x2 = x1 + dx; |
334 | y1 = data->levels[i]; |
335 | y2 = data->levels[i + 1]; |
336 | dy = (s64)y2 - y1; |
337 | |
338 | for (x = x1; x < x2; x++) { |
339 | table[x] = y1 + |
340 | div_s64(dividend: dy * (x - x1), divisor: dx); |
341 | } |
342 | } |
343 | /* Fill in the last point, since no line starts here. */ |
344 | table[x2] = y2; |
345 | |
346 | /* |
347 | * As we use interpolation lets remove current |
348 | * brightness levels table and replace for the |
349 | * new interpolated table. |
350 | */ |
351 | devm_kfree(dev, p: data->levels); |
352 | data->levels = table; |
353 | } |
354 | |
355 | data->max_brightness = num_levels - 1; |
356 | } |
357 | |
358 | return 0; |
359 | } |
360 | |
361 | static const struct of_device_id pwm_backlight_of_match[] = { |
362 | { .compatible = "pwm-backlight" }, |
363 | { } |
364 | }; |
365 | |
366 | MODULE_DEVICE_TABLE(of, pwm_backlight_of_match); |
367 | #else |
368 | static int pwm_backlight_parse_dt(struct device *dev, |
369 | struct platform_pwm_backlight_data *data) |
370 | { |
371 | return -ENODEV; |
372 | } |
373 | |
374 | static |
375 | int pwm_backlight_brightness_default(struct device *dev, |
376 | struct platform_pwm_backlight_data *data, |
377 | unsigned int period) |
378 | { |
379 | return -ENODEV; |
380 | } |
381 | #endif |
382 | |
383 | static bool pwm_backlight_is_linear(struct platform_pwm_backlight_data *data) |
384 | { |
385 | unsigned int nlevels = data->max_brightness + 1; |
386 | unsigned int min_val = data->levels[0]; |
387 | unsigned int max_val = data->levels[nlevels - 1]; |
388 | /* |
389 | * Multiplying by 128 means that even in pathological cases such |
390 | * as (max_val - min_val) == nlevels the error at max_val is less |
391 | * than 1%. |
392 | */ |
393 | unsigned int slope = (128 * (max_val - min_val)) / nlevels; |
394 | unsigned int margin = (max_val - min_val) / 20; /* 5% */ |
395 | int i; |
396 | |
397 | for (i = 1; i < nlevels; i++) { |
398 | unsigned int linear_value = min_val + ((i * slope) / 128); |
399 | unsigned int delta = abs(linear_value - data->levels[i]); |
400 | |
401 | if (delta > margin) |
402 | return false; |
403 | } |
404 | |
405 | return true; |
406 | } |
407 | |
408 | static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb) |
409 | { |
410 | struct device_node *node = pb->dev->of_node; |
411 | bool active = true; |
412 | |
413 | /* |
414 | * If the enable GPIO is present, observable (either as input |
415 | * or output) and off then the backlight is not currently active. |
416 | * */ |
417 | if (pb->enable_gpio && gpiod_get_value_cansleep(desc: pb->enable_gpio) == 0) |
418 | active = false; |
419 | |
420 | if (pb->power_supply && !regulator_is_enabled(regulator: pb->power_supply)) |
421 | active = false; |
422 | |
423 | if (!pwm_is_enabled(pwm: pb->pwm)) |
424 | active = false; |
425 | |
426 | /* |
427 | * Synchronize the enable_gpio with the observed state of the |
428 | * hardware. |
429 | */ |
430 | gpiod_direction_output(desc: pb->enable_gpio, value: active); |
431 | |
432 | /* |
433 | * Do not change pb->enabled here! pb->enabled essentially |
434 | * tells us if we own one of the regulator's use counts and |
435 | * right now we do not. |
436 | */ |
437 | |
438 | /* Not booted with device tree or no phandle link to the node */ |
439 | if (!node || !node->phandle) |
440 | return FB_BLANK_UNBLANK; |
441 | |
442 | /* |
443 | * If the driver is probed from the device tree and there is a |
444 | * phandle link pointing to the backlight node, it is safe to |
445 | * assume that another driver will enable the backlight at the |
446 | * appropriate time. Therefore, if it is disabled, keep it so. |
447 | */ |
448 | return active ? FB_BLANK_UNBLANK: FB_BLANK_POWERDOWN; |
449 | } |
450 | |
451 | static int pwm_backlight_probe(struct platform_device *pdev) |
452 | { |
453 | struct platform_pwm_backlight_data *data = dev_get_platdata(dev: &pdev->dev); |
454 | struct platform_pwm_backlight_data defdata; |
455 | struct backlight_properties props; |
456 | struct backlight_device *bl; |
457 | struct pwm_bl_data *pb; |
458 | struct pwm_state state; |
459 | unsigned int i; |
460 | int ret; |
461 | |
462 | if (!data) { |
463 | ret = pwm_backlight_parse_dt(dev: &pdev->dev, data: &defdata); |
464 | if (ret < 0) { |
465 | dev_err(&pdev->dev, "failed to find platform data\n" ); |
466 | return ret; |
467 | } |
468 | |
469 | data = &defdata; |
470 | } |
471 | |
472 | if (data->init) { |
473 | ret = data->init(&pdev->dev); |
474 | if (ret < 0) |
475 | return ret; |
476 | } |
477 | |
478 | pb = devm_kzalloc(dev: &pdev->dev, size: sizeof(*pb), GFP_KERNEL); |
479 | if (!pb) { |
480 | ret = -ENOMEM; |
481 | goto err_alloc; |
482 | } |
483 | |
484 | pb->notify = data->notify; |
485 | pb->notify_after = data->notify_after; |
486 | pb->check_fb = data->check_fb; |
487 | pb->exit = data->exit; |
488 | pb->dev = &pdev->dev; |
489 | pb->enabled = false; |
490 | pb->post_pwm_on_delay = data->post_pwm_on_delay; |
491 | pb->pwm_off_delay = data->pwm_off_delay; |
492 | |
493 | pb->enable_gpio = devm_gpiod_get_optional(dev: &pdev->dev, con_id: "enable" , |
494 | flags: GPIOD_ASIS); |
495 | if (IS_ERR(ptr: pb->enable_gpio)) { |
496 | ret = PTR_ERR(ptr: pb->enable_gpio); |
497 | goto err_alloc; |
498 | } |
499 | |
500 | pb->power_supply = devm_regulator_get_optional(dev: &pdev->dev, id: "power" ); |
501 | if (IS_ERR(ptr: pb->power_supply)) { |
502 | ret = PTR_ERR(ptr: pb->power_supply); |
503 | if (ret == -ENODEV) |
504 | pb->power_supply = NULL; |
505 | else |
506 | goto err_alloc; |
507 | } |
508 | |
509 | pb->pwm = devm_pwm_get(dev: &pdev->dev, NULL); |
510 | if (IS_ERR(ptr: pb->pwm)) { |
511 | ret = PTR_ERR(ptr: pb->pwm); |
512 | if (ret != -EPROBE_DEFER) |
513 | dev_err(&pdev->dev, "unable to request PWM\n" ); |
514 | goto err_alloc; |
515 | } |
516 | |
517 | dev_dbg(&pdev->dev, "got pwm for backlight\n" ); |
518 | |
519 | /* Sync up PWM state. */ |
520 | pwm_init_state(pwm: pb->pwm, state: &state); |
521 | |
522 | /* |
523 | * The DT case will set the pwm_period_ns field to 0 and store the |
524 | * period, parsed from the DT, in the PWM device. For the non-DT case, |
525 | * set the period from platform data if it has not already been set |
526 | * via the PWM lookup table. |
527 | */ |
528 | if (!state.period && (data->pwm_period_ns > 0)) |
529 | state.period = data->pwm_period_ns; |
530 | |
531 | ret = pwm_apply_state(pwm: pb->pwm, state: &state); |
532 | if (ret) { |
533 | dev_err(&pdev->dev, "failed to apply initial PWM state: %d\n" , |
534 | ret); |
535 | goto err_alloc; |
536 | } |
537 | |
538 | memset(&props, 0, sizeof(struct backlight_properties)); |
539 | |
540 | if (data->levels) { |
541 | pb->levels = data->levels; |
542 | |
543 | /* |
544 | * For the DT case, only when brightness levels is defined |
545 | * data->levels is filled. For the non-DT case, data->levels |
546 | * can come from platform data, however is not usual. |
547 | */ |
548 | for (i = 0; i <= data->max_brightness; i++) |
549 | if (data->levels[i] > pb->scale) |
550 | pb->scale = data->levels[i]; |
551 | |
552 | if (pwm_backlight_is_linear(data)) |
553 | props.scale = BACKLIGHT_SCALE_LINEAR; |
554 | else |
555 | props.scale = BACKLIGHT_SCALE_NON_LINEAR; |
556 | } else if (!data->max_brightness) { |
557 | /* |
558 | * If no brightness levels are provided and max_brightness is |
559 | * not set, use the default brightness table. For the DT case, |
560 | * max_brightness is set to 0 when brightness levels is not |
561 | * specified. For the non-DT case, max_brightness is usually |
562 | * set to some value. |
563 | */ |
564 | |
565 | /* Get the PWM period (in nanoseconds) */ |
566 | pwm_get_state(pwm: pb->pwm, state: &state); |
567 | |
568 | ret = pwm_backlight_brightness_default(dev: &pdev->dev, data, |
569 | period: state.period); |
570 | if (ret < 0) { |
571 | dev_err(&pdev->dev, |
572 | "failed to setup default brightness table\n" ); |
573 | goto err_alloc; |
574 | } |
575 | |
576 | for (i = 0; i <= data->max_brightness; i++) { |
577 | if (data->levels[i] > pb->scale) |
578 | pb->scale = data->levels[i]; |
579 | |
580 | pb->levels = data->levels; |
581 | } |
582 | |
583 | props.scale = BACKLIGHT_SCALE_NON_LINEAR; |
584 | } else { |
585 | /* |
586 | * That only happens for the non-DT case, where platform data |
587 | * sets the max_brightness value. |
588 | */ |
589 | pb->scale = data->max_brightness; |
590 | } |
591 | |
592 | pb->lth_brightness = data->lth_brightness * (div_u64(dividend: state.period, |
593 | divisor: pb->scale)); |
594 | |
595 | props.type = BACKLIGHT_RAW; |
596 | props.max_brightness = data->max_brightness; |
597 | bl = backlight_device_register(name: dev_name(dev: &pdev->dev), dev: &pdev->dev, devdata: pb, |
598 | ops: &pwm_backlight_ops, props: &props); |
599 | if (IS_ERR(ptr: bl)) { |
600 | dev_err(&pdev->dev, "failed to register backlight\n" ); |
601 | ret = PTR_ERR(ptr: bl); |
602 | goto err_alloc; |
603 | } |
604 | |
605 | if (data->dft_brightness > data->max_brightness) { |
606 | dev_warn(&pdev->dev, |
607 | "invalid default brightness level: %u, using %u\n" , |
608 | data->dft_brightness, data->max_brightness); |
609 | data->dft_brightness = data->max_brightness; |
610 | } |
611 | |
612 | bl->props.brightness = data->dft_brightness; |
613 | bl->props.power = pwm_backlight_initial_power_state(pb); |
614 | backlight_update_status(bd: bl); |
615 | |
616 | platform_set_drvdata(pdev, data: bl); |
617 | return 0; |
618 | |
619 | err_alloc: |
620 | if (data->exit) |
621 | data->exit(&pdev->dev); |
622 | return ret; |
623 | } |
624 | |
625 | static void pwm_backlight_remove(struct platform_device *pdev) |
626 | { |
627 | struct backlight_device *bl = platform_get_drvdata(pdev); |
628 | struct pwm_bl_data *pb = bl_get_data(bl_dev: bl); |
629 | struct pwm_state state; |
630 | |
631 | backlight_device_unregister(bd: bl); |
632 | pwm_backlight_power_off(pb); |
633 | pwm_get_state(pwm: pb->pwm, state: &state); |
634 | state.duty_cycle = 0; |
635 | state.enabled = false; |
636 | pwm_apply_state(pwm: pb->pwm, state: &state); |
637 | |
638 | if (pb->exit) |
639 | pb->exit(&pdev->dev); |
640 | } |
641 | |
642 | static void pwm_backlight_shutdown(struct platform_device *pdev) |
643 | { |
644 | struct backlight_device *bl = platform_get_drvdata(pdev); |
645 | struct pwm_bl_data *pb = bl_get_data(bl_dev: bl); |
646 | struct pwm_state state; |
647 | |
648 | pwm_backlight_power_off(pb); |
649 | pwm_get_state(pwm: pb->pwm, state: &state); |
650 | state.duty_cycle = 0; |
651 | state.enabled = false; |
652 | pwm_apply_state(pwm: pb->pwm, state: &state); |
653 | } |
654 | |
655 | #ifdef CONFIG_PM_SLEEP |
656 | static int pwm_backlight_suspend(struct device *dev) |
657 | { |
658 | struct backlight_device *bl = dev_get_drvdata(dev); |
659 | struct pwm_bl_data *pb = bl_get_data(bl_dev: bl); |
660 | struct pwm_state state; |
661 | |
662 | if (pb->notify) |
663 | pb->notify(pb->dev, 0); |
664 | |
665 | pwm_backlight_power_off(pb); |
666 | |
667 | /* |
668 | * Note that disabling the PWM doesn't guarantee that the output stays |
669 | * at its inactive state. However without the PWM disabled, the PWM |
670 | * driver refuses to suspend. So disable here even though this might |
671 | * enable the backlight on poorly designed boards. |
672 | */ |
673 | pwm_get_state(pwm: pb->pwm, state: &state); |
674 | state.duty_cycle = 0; |
675 | state.enabled = false; |
676 | pwm_apply_state(pwm: pb->pwm, state: &state); |
677 | |
678 | if (pb->notify_after) |
679 | pb->notify_after(pb->dev, 0); |
680 | |
681 | return 0; |
682 | } |
683 | |
684 | static int pwm_backlight_resume(struct device *dev) |
685 | { |
686 | struct backlight_device *bl = dev_get_drvdata(dev); |
687 | |
688 | backlight_update_status(bd: bl); |
689 | |
690 | return 0; |
691 | } |
692 | #endif |
693 | |
694 | static const struct dev_pm_ops pwm_backlight_pm_ops = { |
695 | #ifdef CONFIG_PM_SLEEP |
696 | .suspend = pwm_backlight_suspend, |
697 | .resume = pwm_backlight_resume, |
698 | .poweroff = pwm_backlight_suspend, |
699 | .restore = pwm_backlight_resume, |
700 | #endif |
701 | }; |
702 | |
703 | static struct platform_driver pwm_backlight_driver = { |
704 | .driver = { |
705 | .name = "pwm-backlight" , |
706 | .pm = &pwm_backlight_pm_ops, |
707 | .of_match_table = of_match_ptr(pwm_backlight_of_match), |
708 | }, |
709 | .probe = pwm_backlight_probe, |
710 | .remove_new = pwm_backlight_remove, |
711 | .shutdown = pwm_backlight_shutdown, |
712 | }; |
713 | |
714 | module_platform_driver(pwm_backlight_driver); |
715 | |
716 | MODULE_DESCRIPTION("PWM based Backlight Driver" ); |
717 | MODULE_LICENSE("GPL v2" ); |
718 | MODULE_ALIAS("platform:pwm-backlight" ); |
719 | |