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