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 pwm_config(bl
->pwm
, duty
, period
);
154 pwm_disable(bl
->pwm
);
157 static int lp8788_bl_update_status(struct backlight_device
*bl_dev
)
159 struct lp8788_bl
*bl
= bl_get_data(bl_dev
);
160 enum lp8788_bl_ctrl_mode mode
= bl
->mode
;
162 if (bl_dev
->props
.state
& BL_CORE_SUSPENDED
)
163 bl_dev
->props
.brightness
= 0;
165 if (is_brightness_ctrl_by_pwm(mode
)) {
166 int brt
= bl_dev
->props
.brightness
;
167 int max
= bl_dev
->props
.max_brightness
;
169 lp8788_pwm_ctrl(bl
, brt
, max
);
170 } else if (is_brightness_ctrl_by_register(mode
)) {
171 u8 brt
= bl_dev
->props
.brightness
;
173 lp8788_write_byte(bl
->lp
, LP8788_BL_BRIGHTNESS
, brt
);
179 static const struct backlight_ops lp8788_bl_ops
= {
180 .options
= BL_CORE_SUSPENDRESUME
,
181 .update_status
= lp8788_bl_update_status
,
184 static int lp8788_backlight_register(struct lp8788_bl
*bl
)
186 struct backlight_device
*bl_dev
;
187 struct backlight_properties props
;
188 struct lp8788_backlight_platform_data
*pdata
= bl
->pdata
;
192 props
.type
= BACKLIGHT_PLATFORM
;
193 props
.max_brightness
= MAX_BRIGHTNESS
;
195 /* Initial brightness */
197 init_brt
= min_t(int, pdata
->initial_brightness
,
198 props
.max_brightness
);
202 props
.brightness
= init_brt
;
204 /* Backlight device name */
205 if (!pdata
|| !pdata
->name
)
206 name
= DEFAULT_BL_NAME
;
210 bl_dev
= backlight_device_register(name
, bl
->lp
->dev
, bl
,
211 &lp8788_bl_ops
, &props
);
213 return PTR_ERR(bl_dev
);
220 static void lp8788_backlight_unregister(struct lp8788_bl
*bl
)
222 struct backlight_device
*bl_dev
= bl
->bl_dev
;
225 backlight_device_unregister(bl_dev
);
228 static ssize_t
lp8788_get_bl_ctl_mode(struct device
*dev
,
229 struct device_attribute
*attr
, char *buf
)
231 struct lp8788_bl
*bl
= dev_get_drvdata(dev
);
232 enum lp8788_bl_ctrl_mode mode
= bl
->mode
;
235 if (is_brightness_ctrl_by_pwm(mode
))
236 strmode
= "PWM based";
237 else if (is_brightness_ctrl_by_register(mode
))
238 strmode
= "Register based";
240 strmode
= "Invalid mode";
242 return scnprintf(buf
, PAGE_SIZE
, "%s\n", strmode
);
245 static DEVICE_ATTR(bl_ctl_mode
, S_IRUGO
, lp8788_get_bl_ctl_mode
, NULL
);
247 static struct attribute
*lp8788_attributes
[] = {
248 &dev_attr_bl_ctl_mode
.attr
,
252 static const struct attribute_group lp8788_attr_group
= {
253 .attrs
= lp8788_attributes
,
256 static int lp8788_backlight_probe(struct platform_device
*pdev
)
258 struct lp8788
*lp
= dev_get_drvdata(pdev
->dev
.parent
);
259 struct lp8788_bl
*bl
;
262 bl
= devm_kzalloc(lp
->dev
, sizeof(struct lp8788_bl
), GFP_KERNEL
);
268 bl
->pdata
= lp
->pdata
->bl_pdata
;
270 platform_set_drvdata(pdev
, bl
);
272 ret
= lp8788_backlight_configure(bl
);
274 dev_err(lp
->dev
, "backlight config err: %d\n", ret
);
278 ret
= lp8788_backlight_register(bl
);
280 dev_err(lp
->dev
, "register backlight err: %d\n", ret
);
284 ret
= sysfs_create_group(&pdev
->dev
.kobj
, &lp8788_attr_group
);
286 dev_err(lp
->dev
, "register sysfs err: %d\n", ret
);
290 backlight_update_status(bl
->bl_dev
);
295 lp8788_backlight_unregister(bl
);
300 static int lp8788_backlight_remove(struct platform_device
*pdev
)
302 struct lp8788_bl
*bl
= platform_get_drvdata(pdev
);
303 struct backlight_device
*bl_dev
= bl
->bl_dev
;
305 bl_dev
->props
.brightness
= 0;
306 backlight_update_status(bl_dev
);
307 sysfs_remove_group(&pdev
->dev
.kobj
, &lp8788_attr_group
);
308 lp8788_backlight_unregister(bl
);
313 static struct platform_driver lp8788_bl_driver
= {
314 .probe
= lp8788_backlight_probe
,
315 .remove
= lp8788_backlight_remove
,
317 .name
= LP8788_DEV_BACKLIGHT
,
320 module_platform_driver(lp8788_bl_driver
);
322 MODULE_DESCRIPTION("Texas Instruments LP8788 Backlight Driver");
323 MODULE_AUTHOR("Milo Kim");
324 MODULE_LICENSE("GPL");
325 MODULE_ALIAS("platform:lp8788-backlight");