2 * TI LP8788 MFD - backlight driver
4 * Copyright 2012 Texas Instruments
6 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
14 #include <linux/backlight.h>
15 #include <linux/err.h>
16 #include <linux/mfd/lp8788.h>
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <linux/pwm.h>
20 #include <linux/slab.h>
22 /* Register address */
23 #define LP8788_BL_CONFIG 0x96
24 #define LP8788_BL_EN BIT(0)
25 #define LP8788_BL_PWM_INPUT_EN BIT(5)
26 #define LP8788_BL_FULLSCALE_SHIFT 2
27 #define LP8788_BL_DIM_MODE_SHIFT 1
28 #define LP8788_BL_PWM_POLARITY_SHIFT 6
30 #define LP8788_BL_BRIGHTNESS 0x97
32 #define LP8788_BL_RAMP 0x98
33 #define LP8788_BL_RAMP_RISE_SHIFT 4
35 #define MAX_BRIGHTNESS 127
36 #define DEFAULT_BL_NAME "lcd-backlight"
38 struct lp8788_bl_config
{
39 enum lp8788_bl_ctrl_mode bl_mode
;
40 enum lp8788_bl_dim_mode dim_mode
;
41 enum lp8788_bl_full_scale_current full_scale
;
42 enum lp8788_bl_ramp_step rise_time
;
43 enum lp8788_bl_ramp_step fall_time
;
44 enum pwm_polarity pwm_pol
;
49 struct backlight_device
*bl_dev
;
50 struct lp8788_backlight_platform_data
*pdata
;
51 enum lp8788_bl_ctrl_mode mode
;
52 struct pwm_device
*pwm
;
55 static struct lp8788_bl_config default_bl_config
= {
56 .bl_mode
= LP8788_BL_REGISTER_ONLY
,
57 .dim_mode
= LP8788_DIM_EXPONENTIAL
,
58 .full_scale
= LP8788_FULLSCALE_1900uA
,
59 .rise_time
= LP8788_RAMP_8192us
,
60 .fall_time
= LP8788_RAMP_8192us
,
61 .pwm_pol
= PWM_POLARITY_NORMAL
,
64 static inline bool is_brightness_ctrl_by_pwm(enum lp8788_bl_ctrl_mode mode
)
66 return mode
== LP8788_BL_COMB_PWM_BASED
;
69 static inline bool is_brightness_ctrl_by_register(enum lp8788_bl_ctrl_mode mode
)
71 return mode
== LP8788_BL_REGISTER_ONLY
||
72 mode
== LP8788_BL_COMB_REGISTER_BASED
;
75 static int lp8788_backlight_configure(struct lp8788_bl
*bl
)
77 struct lp8788_backlight_platform_data
*pdata
= bl
->pdata
;
78 struct lp8788_bl_config
*cfg
= &default_bl_config
;
83 * Update chip configuration if platform data exists,
84 * otherwise use the default settings.
87 cfg
->bl_mode
= pdata
->bl_mode
;
88 cfg
->dim_mode
= pdata
->dim_mode
;
89 cfg
->full_scale
= pdata
->full_scale
;
90 cfg
->rise_time
= pdata
->rise_time
;
91 cfg
->fall_time
= pdata
->fall_time
;
92 cfg
->pwm_pol
= pdata
->pwm_pol
;
95 /* Brightness ramp up/down */
96 val
= (cfg
->rise_time
<< LP8788_BL_RAMP_RISE_SHIFT
) | cfg
->fall_time
;
97 ret
= lp8788_write_byte(bl
->lp
, LP8788_BL_RAMP
, val
);
101 /* Fullscale current setting */
102 val
= (cfg
->full_scale
<< LP8788_BL_FULLSCALE_SHIFT
) |
103 (cfg
->dim_mode
<< LP8788_BL_DIM_MODE_SHIFT
);
105 /* Brightness control mode */
106 switch (cfg
->bl_mode
) {
107 case LP8788_BL_REGISTER_ONLY
:
110 case LP8788_BL_COMB_PWM_BASED
:
111 case LP8788_BL_COMB_REGISTER_BASED
:
112 val
|= LP8788_BL_EN
| LP8788_BL_PWM_INPUT_EN
|
113 (cfg
->pwm_pol
<< LP8788_BL_PWM_POLARITY_SHIFT
);
116 dev_err(bl
->lp
->dev
, "invalid mode: %d\n", cfg
->bl_mode
);
120 bl
->mode
= cfg
->bl_mode
;
122 return lp8788_write_byte(bl
->lp
, LP8788_BL_CONFIG
, val
);
125 static void lp8788_pwm_ctrl(struct lp8788_bl
*bl
, int br
, int max_br
)
130 struct pwm_device
*pwm
;
135 period
= bl
->pdata
->period_ns
;
136 duty
= br
* period
/ max_br
;
139 /* request PWM device with the consumer name */
141 pwm
= devm_pwm_get(dev
, LP8788_DEV_BACKLIGHT
);
143 dev_err(dev
, "can not get PWM device\n");
150 * FIXME: pwm_apply_args() should be removed when switching to
151 * the atomic PWM API.
156 pwm_config(bl
->pwm
, duty
, period
);
160 pwm_disable(bl
->pwm
);
163 static int lp8788_bl_update_status(struct backlight_device
*bl_dev
)
165 struct lp8788_bl
*bl
= bl_get_data(bl_dev
);
166 enum lp8788_bl_ctrl_mode mode
= bl
->mode
;
168 if (bl_dev
->props
.state
& BL_CORE_SUSPENDED
)
169 bl_dev
->props
.brightness
= 0;
171 if (is_brightness_ctrl_by_pwm(mode
)) {
172 int brt
= bl_dev
->props
.brightness
;
173 int max
= bl_dev
->props
.max_brightness
;
175 lp8788_pwm_ctrl(bl
, brt
, max
);
176 } else if (is_brightness_ctrl_by_register(mode
)) {
177 u8 brt
= bl_dev
->props
.brightness
;
179 lp8788_write_byte(bl
->lp
, LP8788_BL_BRIGHTNESS
, brt
);
185 static const struct backlight_ops lp8788_bl_ops
= {
186 .options
= BL_CORE_SUSPENDRESUME
,
187 .update_status
= lp8788_bl_update_status
,
190 static int lp8788_backlight_register(struct lp8788_bl
*bl
)
192 struct backlight_device
*bl_dev
;
193 struct backlight_properties props
;
194 struct lp8788_backlight_platform_data
*pdata
= bl
->pdata
;
198 props
.type
= BACKLIGHT_PLATFORM
;
199 props
.max_brightness
= MAX_BRIGHTNESS
;
201 /* Initial brightness */
203 init_brt
= min_t(int, pdata
->initial_brightness
,
204 props
.max_brightness
);
208 props
.brightness
= init_brt
;
210 /* Backlight device name */
211 if (!pdata
|| !pdata
->name
)
212 name
= DEFAULT_BL_NAME
;
216 bl_dev
= backlight_device_register(name
, bl
->lp
->dev
, bl
,
217 &lp8788_bl_ops
, &props
);
219 return PTR_ERR(bl_dev
);
226 static void lp8788_backlight_unregister(struct lp8788_bl
*bl
)
228 struct backlight_device
*bl_dev
= bl
->bl_dev
;
230 backlight_device_unregister(bl_dev
);
233 static ssize_t
lp8788_get_bl_ctl_mode(struct device
*dev
,
234 struct device_attribute
*attr
, char *buf
)
236 struct lp8788_bl
*bl
= dev_get_drvdata(dev
);
237 enum lp8788_bl_ctrl_mode mode
= bl
->mode
;
240 if (is_brightness_ctrl_by_pwm(mode
))
241 strmode
= "PWM based";
242 else if (is_brightness_ctrl_by_register(mode
))
243 strmode
= "Register based";
245 strmode
= "Invalid mode";
247 return scnprintf(buf
, PAGE_SIZE
, "%s\n", strmode
);
250 static DEVICE_ATTR(bl_ctl_mode
, S_IRUGO
, lp8788_get_bl_ctl_mode
, NULL
);
252 static struct attribute
*lp8788_attributes
[] = {
253 &dev_attr_bl_ctl_mode
.attr
,
257 static const struct attribute_group lp8788_attr_group
= {
258 .attrs
= lp8788_attributes
,
261 static int lp8788_backlight_probe(struct platform_device
*pdev
)
263 struct lp8788
*lp
= dev_get_drvdata(pdev
->dev
.parent
);
264 struct lp8788_bl
*bl
;
267 bl
= devm_kzalloc(lp
->dev
, sizeof(struct lp8788_bl
), GFP_KERNEL
);
273 bl
->pdata
= lp
->pdata
->bl_pdata
;
275 platform_set_drvdata(pdev
, bl
);
277 ret
= lp8788_backlight_configure(bl
);
279 dev_err(lp
->dev
, "backlight config err: %d\n", ret
);
283 ret
= lp8788_backlight_register(bl
);
285 dev_err(lp
->dev
, "register backlight err: %d\n", ret
);
289 ret
= sysfs_create_group(&pdev
->dev
.kobj
, &lp8788_attr_group
);
291 dev_err(lp
->dev
, "register sysfs err: %d\n", ret
);
295 backlight_update_status(bl
->bl_dev
);
300 lp8788_backlight_unregister(bl
);
305 static int lp8788_backlight_remove(struct platform_device
*pdev
)
307 struct lp8788_bl
*bl
= platform_get_drvdata(pdev
);
308 struct backlight_device
*bl_dev
= bl
->bl_dev
;
310 bl_dev
->props
.brightness
= 0;
311 backlight_update_status(bl_dev
);
312 sysfs_remove_group(&pdev
->dev
.kobj
, &lp8788_attr_group
);
313 lp8788_backlight_unregister(bl
);
318 static struct platform_driver lp8788_bl_driver
= {
319 .probe
= lp8788_backlight_probe
,
320 .remove
= lp8788_backlight_remove
,
322 .name
= LP8788_DEV_BACKLIGHT
,
325 module_platform_driver(lp8788_bl_driver
);
327 MODULE_DESCRIPTION("Texas Instruments LP8788 Backlight Driver");
328 MODULE_AUTHOR("Milo Kim");
329 MODULE_LICENSE("GPL");
330 MODULE_ALIAS("platform:lp8788-backlight");