File Index
Files and directories
Sign in
FILE

tps65217_bl.c

File preview with syntax highlighting.

linux-6.19.14/drivers/video/backlight/tps65217_bl.c 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/mfd/tps65217.h>
 15#include <linux/module.h>
 16#include <linux/platform_device.h>
 17#include <linux/slab.h>
 18
 19struct tps65217_bl {
 20	struct tps65217 *tps;
 21	struct device *dev;
 22	struct backlight_device *bl;
 23	bool is_enabled;
 24};
 25
 26static 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
 46static 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
 67static 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
 95static const struct backlight_ops tps65217_bl_ops = {
 96	.options	= BL_CORE_SUSPENDRESUME,
 97	.update_status	= tps65217_bl_update_status,
 98};
 99
100static 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
166static struct tps65217_bl_pdata *
167tps65217_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
239err:
240	of_node_put(node);
241
242	return err;
243}
244#else
245static struct tps65217_bl_pdata *
246tps65217_bl_parse_dt(struct platform_device *pdev)
247{
248	return NULL;
249}
250#endif
251
252static 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
298static const struct of_device_id tps65217_bl_of_match[] = {
299	{ .compatible = "ti,tps65217-bl", },
300	{ /* sentinel */ },
301};
302MODULE_DEVICE_TABLE(of, tps65217_bl_of_match);
303#endif
304
305static 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
313module_platform_driver(tps65217_bl_driver);
314
315MODULE_DESCRIPTION("TPS65217 Backlight driver");
316MODULE_LICENSE("GPL v2");
317MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");
~/ Read-only file preview.