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 
tps65217_bl_enable(struct tps65217_bl * tps65217_bl)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 
tps65217_bl_disable(struct tps65217_bl * tps65217_bl)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 
tps65217_bl_update_status(struct backlight_device * bl)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 
tps65217_bl_hw_init(struct tps65217_bl * tps65217_bl,struct tps65217_bl_pdata * pdata)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 *
tps65217_bl_parse_dt(struct platform_device * pdev)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 *
tps65217_bl_parse_dt(struct platform_device * pdev)247 tps65217_bl_parse_dt(struct platform_device *pdev)
248 {
249 	return NULL;
250 }
251 #endif
252 
tps65217_bl_probe(struct platform_device * pdev)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>");
319