LP8758 - consolidated version
[mlp-open-source/kernel.git] / drivers / video / backlight / lm3631_bl.c
1 /*
2  * TI LM3631 Backlight Driver
3  *
4  * Copyright 2013 Texas Instruments
5  *
6  * Author: Milo Kim <milo.kim@ti.com>
7  *
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.
11  *
12  */
14 #include <linux/backlight.h>
15 #include <linux/delay.h>
16 #include <linux/err.h>
17 #include <linux/mfd/lm3631.h>
18 #include <linux/module.h>
19 #include <linux/of.h>
20 #include <linux/platform_device.h>
21 #include <linux/pwm.h>
22 #include <linux/slab.h>
24 #define LM3631_MAX_BRIGHTNESS           2047
26 #define DEFAULT_BL_NAME                 "lcd-backlight"
28 enum lm3631_bl_ctrl_mode {
29         LMU_BL_I2C,
30         LMU_BL_PWM,
31 };
33 struct lm3631_bl {
34         struct device *dev;
35         struct backlight_device *bl_dev;
37         struct lm3631 *lm3631;
38         struct lm3631_backlight_platform_data *pdata;
39         enum lm3631_bl_ctrl_mode mode;
41         struct pwm_device *pwm;
42 };
44 static int lm3631_bl_enable(struct lm3631_bl *lm3631_bl, int enable)
45 {
46         return lm3631_update_bits(lm3631_bl->lm3631, LM3631_REG_DEVCTRL,
47                                   LM3631_BL_EN_MASK,
48                                   enable << LM3631_BL_EN_SHIFT);
49 }
51 static void lm3631_bl_pwm_ctrl(struct lm3631_bl *lm3631_bl, int br, int max_br)
52 {
53         unsigned int period;
54         unsigned int duty;
55         struct pwm_device *pwm;
57         if (!lm3631_bl->pdata)
58                 return;
60         period = lm3631_bl->pdata->pwm_period;
61         duty = br * period / max_br;
63         /* Request a PWM device with the consumer name */
64         if (!lm3631_bl->pwm) {
65                 pwm = devm_pwm_get(lm3631_bl->dev, "lm3631-backlight");
66                 if (IS_ERR(pwm)) {
67                         dev_err(lm3631_bl->dev, "can not get PWM device\n");
68                         return;
69                 }
70                 lm3631_bl->pwm = pwm;
71         }
73         pwm_config(lm3631_bl->pwm, duty, period);
74         if (duty)
75                 pwm_enable(lm3631_bl->pwm);
76         else
77                 pwm_disable(lm3631_bl->pwm);
78 }
80 static inline int lm3631_bl_set_brightness(struct lm3631_bl *lm3631_bl, int val)
81 {
82         u8 data;
83         int ret;
85         data = val & LM3631_BRT_LSB_MASK;
86         ret = lm3631_update_bits(lm3631_bl->lm3631, LM3631_REG_BRT_LSB,
87                                  LM3631_BRT_LSB_MASK, data);
88         if (ret)
89                 return ret;
91         data = (val >> LM3631_BRT_MSB_SHIFT) & 0xFF;
92         return lm3631_write_byte(lm3631_bl->lm3631, LM3631_REG_BRT_MSB,
93                                  data);
94 }
96 static int lm3631_bl_update_status(struct backlight_device *bl_dev)
97 {
98         struct lm3631_bl *lm3631_bl = bl_get_data(bl_dev);
99         int brt;
100         int ret;
102         if (bl_dev->props.state & BL_CORE_SUSPENDED)
103                 bl_dev->props.brightness = 0;
105         brt = bl_dev->props.brightness;
107         if (brt > 0)
108                 ret = lm3631_bl_enable(lm3631_bl, 1);
109         else
110                 ret = lm3631_bl_enable(lm3631_bl, 0);
112         if (ret)
113                 return ret;
115         if (lm3631_bl->mode == LMU_BL_PWM)
116                 lm3631_bl_pwm_ctrl(lm3631_bl, brt,
117                                    bl_dev->props.max_brightness);
118         else
119                 ret = lm3631_bl_set_brightness(lm3631_bl, brt);
121         return ret;
124 static int lm3631_bl_get_brightness(struct backlight_device *bl_dev)
126         return bl_dev->props.brightness;
129 static const struct backlight_ops lm3631_bl_ops = {
130         .options = BL_CORE_SUSPENDRESUME,
131         .update_status = lm3631_bl_update_status,
132         .get_brightness = lm3631_bl_get_brightness,
133 };
135 static int lm3631_bl_register(struct lm3631_bl *lm3631_bl)
137         struct backlight_device *bl_dev;
138         struct backlight_properties props;
139         struct lm3631_backlight_platform_data *pdata = lm3631_bl->pdata;
140         char name[20];
142         props.type = BACKLIGHT_PLATFORM;
143         props.brightness = pdata ? pdata->init_brightness : 0;
144         props.max_brightness = LM3631_MAX_BRIGHTNESS;
146         if (!pdata || !pdata->name)
147                 snprintf(name, sizeof(name), "%s", DEFAULT_BL_NAME);
148         else
149                 snprintf(name, sizeof(name), "%s", pdata->name);
151         bl_dev = backlight_device_register(name, lm3631_bl->dev, lm3631_bl,
152                                            &lm3631_bl_ops, &props);
153         if (IS_ERR(bl_dev))
154                 return PTR_ERR(bl_dev);
156         lm3631_bl->bl_dev = bl_dev;
158         return 0;
161 static void lm3631_bl_unregister(struct lm3631_bl *lm3631_bl)
163         if (lm3631_bl->bl_dev)
164                 backlight_device_unregister(lm3631_bl->bl_dev);
167 static int lm3631_bl_set_ctrl_mode(struct lm3631_bl *lm3631_bl)
169         struct lm3631_backlight_platform_data *pdata = lm3631_bl->pdata;
171         /* Brightness control mode is I2C only by default */
172         if (!pdata) {
173                 lm3631_bl->mode = LMU_BL_I2C;
174                 return lm3631_update_bits(lm3631_bl->lm3631,
175                                           LM3631_REG_BRT_MODE, LM3631_BRT_MASK,
176                                           LM3631_I2C_ONLY);
177         }
179         if (pdata->pwm_period > 0)
180                 lm3631_bl->mode = LMU_BL_PWM;
182         return lm3631_update_bits(lm3631_bl->lm3631, LM3631_REG_BRT_MODE,
183                                   LM3631_BRT_MASK, pdata->mode);
186 static int lm3631_bl_string_configure(struct lm3631_bl *lm3631_bl)
188         u8 val;
190         if (lm3631_bl->pdata->is_full_strings)
191                 val = LM3631_BL_TWO_STRINGS;
192         else
193                 val = LM3631_BL_ONE_STRING;
195         return lm3631_update_bits(lm3631_bl->lm3631, LM3631_REG_BL_CFG,
196                                   LM3631_BL_STRING_MASK, val);
199 static int lm3631_bl_configure(struct lm3631_bl *lm3631_bl)
201         int ret;
203         ret = lm3631_bl_set_ctrl_mode(lm3631_bl);
204         if (ret)
205                 return ret;
207         return lm3631_bl_string_configure(lm3631_bl);
210 static int lm3631_bl_parse_dt(struct device *dev, struct lm3631_bl *lm3631_bl)
212         struct device_node *node = dev->of_node;
213         struct lm3631_backlight_platform_data *pdata;
215         pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
216         if (!pdata)
217                 return -ENOMEM;
219         of_property_read_string(node, "bl-name", &pdata->name);
221         if (of_find_property(node, "full-strings-used", NULL))
222                 pdata->is_full_strings = true;
224         if (of_find_property(node, "mode-pwm-only", NULL))
225                 pdata->mode = LM3631_PWM_ONLY;
226         else if (of_find_property(node, "mode-comb1", NULL))
227                 pdata->mode = LM3631_COMB1;
228         else if (of_find_property(node, "mode-comb2", NULL))
229                 pdata->mode = LM3631_COMB2;
231         of_property_read_u8(node, "initial-brightness",
232                             (u8 *)&pdata->init_brightness);
234         of_property_read_u32(node, "pwm-period", &pdata->pwm_period);
236         lm3631_bl->pdata = pdata;
238         return 0;
241 static int lm3631_bl_probe(struct platform_device *pdev)
243         struct lm3631 *lm3631 = dev_get_drvdata(pdev->dev.parent);
244         struct lm3631_backlight_platform_data *pdata = lm3631->pdata->bl_pdata;
245         struct lm3631_bl *lm3631_bl;
246         int ret;
248         lm3631_bl = devm_kzalloc(&pdev->dev, sizeof(*lm3631_bl), GFP_KERNEL);
249         if (!lm3631_bl)
250                 return -ENOMEM;
252         lm3631_bl->pdata = pdata;
253         if (!lm3631_bl->pdata) {
254                 if (IS_ENABLED(CONFIG_OF))
255                         ret = lm3631_bl_parse_dt(&pdev->dev, lm3631_bl);
256                 else
257                         return -ENODEV;
259                 if (ret)
260                         return ret;
261         }
263         lm3631_bl->dev = &pdev->dev;
264         lm3631_bl->lm3631 = lm3631;
265         platform_set_drvdata(pdev, lm3631_bl);
267         ret = lm3631_bl_configure(lm3631_bl);
268         if (ret) {
269                 dev_err(&pdev->dev, "backlight config err: %d\n", ret);
270                 return ret;
271         }
273         ret = lm3631_bl_register(lm3631_bl);
274         if (ret) {
275                 dev_err(&pdev->dev, "register backlight err: %d\n", ret);
276                 return ret;
277         }
279         backlight_update_status(lm3631_bl->bl_dev);
281         return 0;
284 static int lm3631_bl_remove(struct platform_device *pdev)
286         struct lm3631_bl *lm3631_bl = platform_get_drvdata(pdev);
287         struct backlight_device *bl_dev = lm3631_bl->bl_dev;
289         bl_dev->props.brightness = 0;
290         backlight_update_status(bl_dev);
291         lm3631_bl_unregister(lm3631_bl);
293         return 0;
296 #ifdef CONFIG_OF
297 static const struct of_device_id lm3631_bl_of_match[] = {
298         { .compatible = "ti,lm3631-backlight", },
299         { }
300 };
301 MODULE_DEVICE_TABLE(of, lm3631_bl_of_match);
302 #endif
304 static struct platform_driver lm3631_bl_driver = {
305         .probe = lm3631_bl_probe,
306         .remove = lm3631_bl_remove,
307         .driver = {
308                 .name = "lm3631-backlight",
309                 .owner = THIS_MODULE,
310                 .of_match_table = of_match_ptr(lm3631_bl_of_match),
311         },
312 };
313 module_platform_driver(lm3631_bl_driver);
315 MODULE_DESCRIPTION("TI LM3631 Backlight Driver");
316 MODULE_AUTHOR("Milo Kim");
317 MODULE_LICENSE("GPL");
318 MODULE_ALIAS("platform:lm3631-backlight");