1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * tps65217_bl.c
4 *
5 * TPS65217 backlight driver
6 *
7 * Copyright (C) 2012 Matthias Kaehlcke
8 * Author: Matthias Kaehlcke <matthias@kaehlcke.net>
9 */
10
11 #include <linux/kernel.h>
12 #include <linux/backlight.h>
13 #include <linux/err.h>
14 #include <linux/mfd/tps65217.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
18
19 struct tps65217_bl {
20 struct tps65217 *tps;
21 struct device *dev;
22 struct backlight_device *bl;
23 bool is_enabled;
24 };
25
tps65217_bl_enable(struct tps65217_bl * tps65217_bl)26 static int tps65217_bl_enable(struct tps65217_bl *tps65217_bl)
27 {
28 int rc;
29
30 rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
31 TPS65217_WLEDCTRL1_ISINK_ENABLE,
32 TPS65217_WLEDCTRL1_ISINK_ENABLE, TPS65217_PROTECT_NONE);
33 if (rc) {
34 dev_err(tps65217_bl->dev,
35 "failed to enable backlight: %d\n", rc);
36 return rc;
37 }
38
39 tps65217_bl->is_enabled = true;
40
41 dev_dbg(tps65217_bl->dev, "backlight enabled\n");
42
43 return 0;
44 }
45
tps65217_bl_disable(struct tps65217_bl * tps65217_bl)46 static int tps65217_bl_disable(struct tps65217_bl *tps65217_bl)
47 {
48 int rc;
49
50 rc = tps65217_clear_bits(tps65217_bl->tps,
51 TPS65217_REG_WLEDCTRL1,
52 TPS65217_WLEDCTRL1_ISINK_ENABLE,
53 TPS65217_PROTECT_NONE);
54 if (rc) {
55 dev_err(tps65217_bl->dev,
56 "failed to disable backlight: %d\n", rc);
57 return rc;
58 }
59
60 tps65217_bl->is_enabled = false;
61
62 dev_dbg(tps65217_bl->dev, "backlight disabled\n");
63
64 return 0;
65 }
66
tps65217_bl_update_status(struct backlight_device * bl)67 static int tps65217_bl_update_status(struct backlight_device *bl)
68 {
69 struct tps65217_bl *tps65217_bl = bl_get_data(bl);
70 int rc;
71 int brightness = backlight_get_brightness(bl);
72
73 if (brightness > 0) {
74 rc = tps65217_reg_write(tps65217_bl->tps,
75 TPS65217_REG_WLEDCTRL2,
76 brightness - 1,
77 TPS65217_PROTECT_NONE);
78 if (rc) {
79 dev_err(tps65217_bl->dev,
80 "failed to set brightness level: %d\n", rc);
81 return rc;
82 }
83
84 dev_dbg(tps65217_bl->dev, "brightness set to %d\n", brightness);
85
86 if (!tps65217_bl->is_enabled)
87 rc = tps65217_bl_enable(tps65217_bl);
88 } else {
89 rc = tps65217_bl_disable(tps65217_bl);
90 }
91
92 return rc;
93 }
94
95 static const struct backlight_ops tps65217_bl_ops = {
96 .options = BL_CORE_SUSPENDRESUME,
97 .update_status = tps65217_bl_update_status,
98 };
99
tps65217_bl_hw_init(struct tps65217_bl * tps65217_bl,struct tps65217_bl_pdata * pdata)100 static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl,
101 struct tps65217_bl_pdata *pdata)
102 {
103 int rc;
104
105 rc = tps65217_bl_disable(tps65217_bl);
106 if (rc)
107 return rc;
108
109 switch (pdata->isel) {
110 case TPS65217_BL_ISET1:
111 /* select ISET_1 current level */
112 rc = tps65217_clear_bits(tps65217_bl->tps,
113 TPS65217_REG_WLEDCTRL1,
114 TPS65217_WLEDCTRL1_ISEL,
115 TPS65217_PROTECT_NONE);
116 if (rc) {
117 dev_err(tps65217_bl->dev,
118 "failed to select ISET1 current level: %d)\n",
119 rc);
120 return rc;
121 }
122
123 dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n");
124
125 break;
126
127 case TPS65217_BL_ISET2:
128 /* select ISET2 current level */
129 rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
130 TPS65217_WLEDCTRL1_ISEL,
131 TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE);
132 if (rc) {
133 dev_err(tps65217_bl->dev,
134 "failed to select ISET2 current level: %d\n",
135 rc);
136 return rc;
137 }
138
139 dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n");
140
141 break;
142
143 default:
144 dev_err(tps65217_bl->dev,
145 "invalid value for current level: %d\n", pdata->isel);
146 return -EINVAL;
147 }
148
149 /* set PWM frequency */
150 rc = tps65217_set_bits(tps65217_bl->tps,
151 TPS65217_REG_WLEDCTRL1,
152 TPS65217_WLEDCTRL1_FDIM_MASK,
153 pdata->fdim,
154 TPS65217_PROTECT_NONE);
155 if (rc) {
156 dev_err(tps65217_bl->dev,
157 "failed to select PWM dimming frequency: %d\n",
158 rc);
159 return rc;
160 }
161
162 return 0;
163 }
164
165 #ifdef CONFIG_OF
166 static struct tps65217_bl_pdata *
tps65217_bl_parse_dt(struct platform_device * pdev)167 tps65217_bl_parse_dt(struct platform_device *pdev)
168 {
169 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
170 struct device_node *node;
171 struct tps65217_bl_pdata *pdata, *err;
172 u32 val;
173
174 node = of_get_child_by_name(tps->dev->of_node, "backlight");
175 if (!node)
176 return ERR_PTR(-ENODEV);
177
178 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
179 if (!pdata) {
180 err = ERR_PTR(-ENOMEM);
181 goto err;
182 }
183
184 pdata->isel = TPS65217_BL_ISET1;
185 if (!of_property_read_u32(node, "isel", &val)) {
186 if (val < TPS65217_BL_ISET1 ||
187 val > TPS65217_BL_ISET2) {
188 dev_err(&pdev->dev,
189 "invalid 'isel' value in the device tree\n");
190 err = ERR_PTR(-EINVAL);
191 goto err;
192 }
193
194 pdata->isel = val;
195 }
196
197 pdata->fdim = TPS65217_BL_FDIM_200HZ;
198 if (!of_property_read_u32(node, "fdim", &val)) {
199 switch (val) {
200 case 100:
201 pdata->fdim = TPS65217_BL_FDIM_100HZ;
202 break;
203
204 case 200:
205 pdata->fdim = TPS65217_BL_FDIM_200HZ;
206 break;
207
208 case 500:
209 pdata->fdim = TPS65217_BL_FDIM_500HZ;
210 break;
211
212 case 1000:
213 pdata->fdim = TPS65217_BL_FDIM_1000HZ;
214 break;
215
216 default:
217 dev_err(&pdev->dev,
218 "invalid 'fdim' value in the device tree\n");
219 err = ERR_PTR(-EINVAL);
220 goto err;
221 }
222 }
223
224 if (!of_property_read_u32(node, "default-brightness", &val)) {
225 if (val > 100) {
226 dev_err(&pdev->dev,
227 "invalid 'default-brightness' value in the device tree\n");
228 err = ERR_PTR(-EINVAL);
229 goto err;
230 }
231
232 pdata->dft_brightness = val;
233 }
234
235 of_node_put(node);
236
237 return pdata;
238
239 err:
240 of_node_put(node);
241
242 return err;
243 }
244 #else
245 static struct tps65217_bl_pdata *
tps65217_bl_parse_dt(struct platform_device * pdev)246 tps65217_bl_parse_dt(struct platform_device *pdev)
247 {
248 return NULL;
249 }
250 #endif
251
tps65217_bl_probe(struct platform_device * pdev)252 static int tps65217_bl_probe(struct platform_device *pdev)
253 {
254 int rc;
255 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
256 struct tps65217_bl *tps65217_bl;
257 struct tps65217_bl_pdata *pdata;
258 struct backlight_properties bl_props;
259
260 pdata = tps65217_bl_parse_dt(pdev);
261 if (IS_ERR(pdata))
262 return PTR_ERR(pdata);
263
264 tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl),
265 GFP_KERNEL);
266 if (tps65217_bl == NULL)
267 return -ENOMEM;
268
269 tps65217_bl->tps = tps;
270 tps65217_bl->dev = &pdev->dev;
271 tps65217_bl->is_enabled = false;
272
273 rc = tps65217_bl_hw_init(tps65217_bl, pdata);
274 if (rc)
275 return rc;
276
277 memset(&bl_props, 0, sizeof(struct backlight_properties));
278 bl_props.type = BACKLIGHT_RAW;
279 bl_props.max_brightness = 100;
280
281 tps65217_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name,
282 tps65217_bl->dev, tps65217_bl,
283 &tps65217_bl_ops, &bl_props);
284 if (IS_ERR(tps65217_bl->bl)) {
285 dev_err(tps65217_bl->dev,
286 "registration of backlight device failed: %d\n", rc);
287 return PTR_ERR(tps65217_bl->bl);
288 }
289
290 tps65217_bl->bl->props.brightness = pdata->dft_brightness;
291 backlight_update_status(tps65217_bl->bl);
292 platform_set_drvdata(pdev, tps65217_bl);
293
294 return 0;
295 }
296
297 #ifdef CONFIG_OF
298 static const struct of_device_id tps65217_bl_of_match[] = {
299 { .compatible = "ti,tps65217-bl", },
300 { /* sentinel */ },
301 };
302 MODULE_DEVICE_TABLE(of, tps65217_bl_of_match);
303 #endif
304
305 static struct platform_driver tps65217_bl_driver = {
306 .probe = tps65217_bl_probe,
307 .driver = {
308 .name = "tps65217-bl",
309 .of_match_table = of_match_ptr(tps65217_bl_of_match),
310 },
311 };
312
313 module_platform_driver(tps65217_bl_driver);
314
315 MODULE_DESCRIPTION("TPS65217 Backlight driver");
316 MODULE_LICENSE("GPL v2");
317 MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");
318