1 /*
2  * Backlight Driver for Dialog DA9052 PMICs
3  *
4  * Copyright(c) 2012 Dialog Semiconductor Ltd.
5  *
6  * Author: David Dajun Chen <dchen@diasemi.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 as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  */
14 
15 #include <linux/backlight.h>
16 #include <linux/delay.h>
17 #include <linux/fb.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 
21 #include <linux/mfd/da9052/da9052.h>
22 #include <linux/mfd/da9052/reg.h>
23 
24 #define DA9052_MAX_BRIGHTNESS		0xFF
25 
26 enum {
27 	DA9052_WLEDS_OFF,
28 	DA9052_WLEDS_ON,
29 };
30 
31 enum {
32 	DA9052_TYPE_WLED1,
33 	DA9052_TYPE_WLED2,
34 	DA9052_TYPE_WLED3,
35 };
36 
37 static const unsigned char wled_bank[] = {
38 	DA9052_LED1_CONF_REG,
39 	DA9052_LED2_CONF_REG,
40 	DA9052_LED3_CONF_REG,
41 };
42 
43 struct da9052_bl {
44 	struct da9052 *da9052;
45 	uint brightness;
46 	uint state;
47 	uint led_reg;
48 };
49 
50 static int da9052_adjust_wled_brightness(struct da9052_bl *wleds)
51 {
52 	unsigned char boost_en;
53 	unsigned char i_sink;
54 	int ret;
55 
56 	boost_en = 0x3F;
57 	i_sink = 0xFF;
58 	if (wleds->state == DA9052_WLEDS_OFF) {
59 		boost_en = 0x00;
60 		i_sink = 0x00;
61 	}
62 
63 	ret = da9052_reg_write(wleds->da9052, DA9052_BOOST_REG, boost_en);
64 	if (ret < 0)
65 		return ret;
66 
67 	ret = da9052_reg_write(wleds->da9052, DA9052_LED_CONT_REG, i_sink);
68 	if (ret < 0)
69 		return ret;
70 
71 	ret = da9052_reg_write(wleds->da9052, wled_bank[wleds->led_reg], 0x0);
72 	if (ret < 0)
73 		return ret;
74 
75 	usleep_range(10000, 11000);
76 
77 	if (wleds->brightness) {
78 		ret = da9052_reg_write(wleds->da9052, wled_bank[wleds->led_reg],
79 				       wleds->brightness);
80 		if (ret < 0)
81 			return ret;
82 	}
83 
84 	return 0;
85 }
86 
87 static int da9052_backlight_update_status(struct backlight_device *bl)
88 {
89 	int brightness = bl->props.brightness;
90 	struct da9052_bl *wleds = bl_get_data(bl);
91 
92 	wleds->brightness = brightness;
93 	wleds->state = DA9052_WLEDS_ON;
94 
95 	return da9052_adjust_wled_brightness(wleds);
96 }
97 
98 static int da9052_backlight_get_brightness(struct backlight_device *bl)
99 {
100 	struct da9052_bl *wleds = bl_get_data(bl);
101 
102 	return wleds->brightness;
103 }
104 
105 static const struct backlight_ops da9052_backlight_ops = {
106 	.update_status = da9052_backlight_update_status,
107 	.get_brightness = da9052_backlight_get_brightness,
108 };
109 
110 static int da9052_backlight_probe(struct platform_device *pdev)
111 {
112 	struct backlight_device *bl;
113 	struct backlight_properties props;
114 	struct da9052_bl *wleds;
115 
116 	wleds = devm_kzalloc(&pdev->dev, sizeof(struct da9052_bl), GFP_KERNEL);
117 	if (!wleds)
118 		return -ENOMEM;
119 
120 	wleds->da9052 = dev_get_drvdata(pdev->dev.parent);
121 	wleds->brightness = 0;
122 	wleds->led_reg = platform_get_device_id(pdev)->driver_data;
123 	wleds->state = DA9052_WLEDS_OFF;
124 
125 	props.type = BACKLIGHT_RAW;
126 	props.max_brightness = DA9052_MAX_BRIGHTNESS;
127 
128 	bl = devm_backlight_device_register(&pdev->dev, pdev->name,
129 					wleds->da9052->dev, wleds,
130 					&da9052_backlight_ops, &props);
131 	if (IS_ERR(bl)) {
132 		dev_err(&pdev->dev, "Failed to register backlight\n");
133 		return PTR_ERR(bl);
134 	}
135 
136 	bl->props.max_brightness = DA9052_MAX_BRIGHTNESS;
137 	bl->props.brightness = 0;
138 	platform_set_drvdata(pdev, bl);
139 
140 	return da9052_adjust_wled_brightness(wleds);
141 }
142 
143 static int da9052_backlight_remove(struct platform_device *pdev)
144 {
145 	struct backlight_device *bl = platform_get_drvdata(pdev);
146 	struct da9052_bl *wleds = bl_get_data(bl);
147 
148 	wleds->brightness = 0;
149 	wleds->state = DA9052_WLEDS_OFF;
150 	da9052_adjust_wled_brightness(wleds);
151 
152 	return 0;
153 }
154 
155 static const struct platform_device_id da9052_wled_ids[] = {
156 	{
157 		.name		= "da9052-wled1",
158 		.driver_data	= DA9052_TYPE_WLED1,
159 	},
160 	{
161 		.name		= "da9052-wled2",
162 		.driver_data	= DA9052_TYPE_WLED2,
163 	},
164 	{
165 		.name		= "da9052-wled3",
166 		.driver_data	= DA9052_TYPE_WLED3,
167 	},
168 	{ },
169 };
170 MODULE_DEVICE_TABLE(platform, da9052_wled_ids);
171 
172 static struct platform_driver da9052_wled_driver = {
173 	.probe		= da9052_backlight_probe,
174 	.remove		= da9052_backlight_remove,
175 	.id_table	= da9052_wled_ids,
176 	.driver	= {
177 		.name	= "da9052-wled",
178 	},
179 };
180 
181 module_platform_driver(da9052_wled_driver);
182 
183 MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
184 MODULE_DESCRIPTION("Backlight driver for DA9052 PMIC");
185 MODULE_LICENSE("GPL");
186