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