File Index
Files and directories
Sign in
FILE

adp5520_bl.c

File preview with syntax highlighting.

linux-6.19.14/drivers/video/backlight/adp5520_bl.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Backlight driver for Analog Devices ADP5520/ADP5501 MFD PMICs
  4 *
  5 * Copyright 2009 Analog Devices Inc.
  6 */
  7
  8#include <linux/kernel.h>
  9#include <linux/init.h>
 10#include <linux/platform_device.h>
 11#include <linux/backlight.h>
 12#include <linux/mfd/adp5520.h>
 13#include <linux/slab.h>
 14#include <linux/module.h>
 15
 16struct adp5520_bl {
 17	struct device *master;
 18	struct adp5520_backlight_platform_data *pdata;
 19	struct mutex lock;
 20	unsigned long cached_daylight_max;
 21	int id;
 22	int current_brightness;
 23};
 24
 25static int adp5520_bl_set(struct backlight_device *bl, int brightness)
 26{
 27	struct adp5520_bl *data = bl_get_data(bl);
 28	struct device *master = data->master;
 29	int ret = 0;
 30
 31	if (data->pdata->en_ambl_sens) {
 32		if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) {
 33			/* Disable Ambient Light auto adjust */
 34			ret |= adp5520_clr_bits(master, ADP5520_BL_CONTROL,
 35					ADP5520_BL_AUTO_ADJ);
 36			ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
 37					brightness);
 38		} else {
 39			/*
 40			 * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
 41			 * restore daylight l3 sysfs brightness
 42			 */
 43			ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
 44					 data->cached_daylight_max);
 45			ret |= adp5520_set_bits(master, ADP5520_BL_CONTROL,
 46					 ADP5520_BL_AUTO_ADJ);
 47		}
 48	} else {
 49		ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, brightness);
 50	}
 51
 52	if (data->current_brightness && brightness == 0)
 53		ret |= adp5520_set_bits(master,
 54				ADP5520_MODE_STATUS, ADP5520_DIM_EN);
 55	else if (data->current_brightness == 0 && brightness)
 56		ret |= adp5520_clr_bits(master,
 57				ADP5520_MODE_STATUS, ADP5520_DIM_EN);
 58
 59	if (!ret)
 60		data->current_brightness = brightness;
 61
 62	return ret;
 63}
 64
 65static int adp5520_bl_update_status(struct backlight_device *bl)
 66{
 67	return adp5520_bl_set(bl, backlight_get_brightness(bl));
 68}
 69
 70static int adp5520_bl_get_brightness(struct backlight_device *bl)
 71{
 72	struct adp5520_bl *data = bl_get_data(bl);
 73	int error;
 74	uint8_t reg_val;
 75
 76	error = adp5520_read(data->master, ADP5520_BL_VALUE, &reg_val);
 77
 78	return error ? data->current_brightness : reg_val;
 79}
 80
 81static const struct backlight_ops adp5520_bl_ops = {
 82	.update_status	= adp5520_bl_update_status,
 83	.get_brightness	= adp5520_bl_get_brightness,
 84};
 85
 86static int adp5520_bl_setup(struct backlight_device *bl)
 87{
 88	struct adp5520_bl *data = bl_get_data(bl);
 89	struct device *master = data->master;
 90	struct adp5520_backlight_platform_data *pdata = data->pdata;
 91	int ret = 0;
 92
 93	ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
 94				pdata->l1_daylight_max);
 95	ret |= adp5520_write(master, ADP5520_DAYLIGHT_DIM,
 96				pdata->l1_daylight_dim);
 97
 98	if (pdata->en_ambl_sens) {
 99		data->cached_daylight_max = pdata->l1_daylight_max;
100		ret |= adp5520_write(master, ADP5520_OFFICE_MAX,
101				pdata->l2_office_max);
102		ret |= adp5520_write(master, ADP5520_OFFICE_DIM,
103				pdata->l2_office_dim);
104		ret |= adp5520_write(master, ADP5520_DARK_MAX,
105				pdata->l3_dark_max);
106		ret |= adp5520_write(master, ADP5520_DARK_DIM,
107				pdata->l3_dark_dim);
108		ret |= adp5520_write(master, ADP5520_L2_TRIP,
109				pdata->l2_trip);
110		ret |= adp5520_write(master, ADP5520_L2_HYS,
111				pdata->l2_hyst);
112		ret |= adp5520_write(master, ADP5520_L3_TRIP,
113				 pdata->l3_trip);
114		ret |= adp5520_write(master, ADP5520_L3_HYS,
115				pdata->l3_hyst);
116		ret |= adp5520_write(master, ADP5520_ALS_CMPR_CFG,
117				ALS_CMPR_CFG_VAL(pdata->abml_filt,
118				ADP5520_L3_EN));
119	}
120
121	ret |= adp5520_write(master, ADP5520_BL_CONTROL,
122			BL_CTRL_VAL(pdata->fade_led_law,
123					pdata->en_ambl_sens));
124
125	ret |= adp5520_write(master, ADP5520_BL_FADE, FADE_VAL(pdata->fade_in,
126			pdata->fade_out));
127
128	ret |= adp5520_set_bits(master, ADP5520_MODE_STATUS,
129			ADP5520_BL_EN | ADP5520_DIM_EN);
130
131	return ret;
132}
133
134static ssize_t adp5520_show(struct device *dev, char *buf, int reg)
135{
136	struct adp5520_bl *data = dev_get_drvdata(dev);
137	int ret;
138	uint8_t reg_val;
139
140	mutex_lock(&data->lock);
141	ret = adp5520_read(data->master, reg, &reg_val);
142	mutex_unlock(&data->lock);
143
144	if (ret < 0)
145		return ret;
146
147	return sprintf(buf, "%u\n", reg_val);
148}
149
150static ssize_t adp5520_store(struct device *dev, const char *buf,
151			 size_t count, int reg)
152{
153	struct adp5520_bl *data = dev_get_drvdata(dev);
154	unsigned long val;
155	int ret;
156
157	ret = kstrtoul(buf, 10, &val);
158	if (ret)
159		return ret;
160
161	mutex_lock(&data->lock);
162	adp5520_write(data->master, reg, val);
163	mutex_unlock(&data->lock);
164
165	return count;
166}
167
168static ssize_t adp5520_bl_dark_max_show(struct device *dev,
169			struct device_attribute *attr, char *buf)
170{
171	return adp5520_show(dev, buf, ADP5520_DARK_MAX);
172}
173
174static ssize_t adp5520_bl_dark_max_store(struct device *dev,
175			struct device_attribute *attr,
176			const char *buf, size_t count)
177{
178	return adp5520_store(dev, buf, count, ADP5520_DARK_MAX);
179}
180static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show,
181			adp5520_bl_dark_max_store);
182
183static ssize_t adp5520_bl_office_max_show(struct device *dev,
184			struct device_attribute *attr, char *buf)
185{
186	return adp5520_show(dev, buf, ADP5520_OFFICE_MAX);
187}
188
189static ssize_t adp5520_bl_office_max_store(struct device *dev,
190			struct device_attribute *attr,
191			const char *buf, size_t count)
192{
193	return adp5520_store(dev, buf, count, ADP5520_OFFICE_MAX);
194}
195static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show,
196			adp5520_bl_office_max_store);
197
198static ssize_t adp5520_bl_daylight_max_show(struct device *dev,
199			struct device_attribute *attr, char *buf)
200{
201	return adp5520_show(dev, buf, ADP5520_DAYLIGHT_MAX);
202}
203
204static ssize_t adp5520_bl_daylight_max_store(struct device *dev,
205			struct device_attribute *attr,
206			const char *buf, size_t count)
207{
208	struct adp5520_bl *data = dev_get_drvdata(dev);
209	int ret;
210
211	ret = kstrtoul(buf, 10, &data->cached_daylight_max);
212	if (ret < 0)
213		return ret;
214
215	return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_MAX);
216}
217static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show,
218			adp5520_bl_daylight_max_store);
219
220static ssize_t adp5520_bl_dark_dim_show(struct device *dev,
221			struct device_attribute *attr, char *buf)
222{
223	return adp5520_show(dev, buf, ADP5520_DARK_DIM);
224}
225
226static ssize_t adp5520_bl_dark_dim_store(struct device *dev,
227			struct device_attribute *attr,
228			const char *buf, size_t count)
229{
230	return adp5520_store(dev, buf, count, ADP5520_DARK_DIM);
231}
232static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show,
233			adp5520_bl_dark_dim_store);
234
235static ssize_t adp5520_bl_office_dim_show(struct device *dev,
236			struct device_attribute *attr, char *buf)
237{
238	return adp5520_show(dev, buf, ADP5520_OFFICE_DIM);
239}
240
241static ssize_t adp5520_bl_office_dim_store(struct device *dev,
242			struct device_attribute *attr,
243			const char *buf, size_t count)
244{
245	return adp5520_store(dev, buf, count, ADP5520_OFFICE_DIM);
246}
247static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show,
248			adp5520_bl_office_dim_store);
249
250static ssize_t adp5520_bl_daylight_dim_show(struct device *dev,
251			struct device_attribute *attr, char *buf)
252{
253	return adp5520_show(dev, buf, ADP5520_DAYLIGHT_DIM);
254}
255
256static ssize_t adp5520_bl_daylight_dim_store(struct device *dev,
257			struct device_attribute *attr,
258			const char *buf, size_t count)
259{
260	return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_DIM);
261}
262static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show,
263			adp5520_bl_daylight_dim_store);
264
265static struct attribute *adp5520_bl_attributes[] = {
266	&dev_attr_dark_max.attr,
267	&dev_attr_dark_dim.attr,
268	&dev_attr_office_max.attr,
269	&dev_attr_office_dim.attr,
270	&dev_attr_daylight_max.attr,
271	&dev_attr_daylight_dim.attr,
272	NULL
273};
274
275static const struct attribute_group adp5520_bl_attr_group = {
276	.attrs = adp5520_bl_attributes,
277};
278
279static int adp5520_bl_probe(struct platform_device *pdev)
280{
281	struct backlight_properties props;
282	struct backlight_device *bl;
283	struct adp5520_bl *data;
284	int ret = 0;
285
286	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
287	if (data == NULL)
288		return -ENOMEM;
289
290	data->master = pdev->dev.parent;
291	data->pdata = dev_get_platdata(&pdev->dev);
292
293	if (data->pdata  == NULL) {
294		dev_err(&pdev->dev, "missing platform data\n");
295		return -ENODEV;
296	}
297
298	data->id = pdev->id;
299	data->current_brightness = 0;
300
301	mutex_init(&data->lock);
302
303	memset(&props, 0, sizeof(struct backlight_properties));
304	props.type = BACKLIGHT_RAW;
305	props.max_brightness = ADP5020_MAX_BRIGHTNESS;
306	bl = devm_backlight_device_register(&pdev->dev, pdev->name,
307					data->master, data, &adp5520_bl_ops,
308					&props);
309	if (IS_ERR(bl)) {
310		dev_err(&pdev->dev, "failed to register backlight\n");
311		return PTR_ERR(bl);
312	}
313
314	bl->props.brightness = ADP5020_MAX_BRIGHTNESS;
315	if (data->pdata->en_ambl_sens)
316		ret = sysfs_create_group(&bl->dev.kobj,
317			&adp5520_bl_attr_group);
318
319	if (ret) {
320		dev_err(&pdev->dev, "failed to register sysfs\n");
321		return ret;
322	}
323
324	platform_set_drvdata(pdev, bl);
325	ret = adp5520_bl_setup(bl);
326	if (ret) {
327		dev_err(&pdev->dev, "failed to setup\n");
328		if (data->pdata->en_ambl_sens)
329			sysfs_remove_group(&bl->dev.kobj,
330					&adp5520_bl_attr_group);
331		return ret;
332	}
333
334	backlight_update_status(bl);
335
336	return 0;
337}
338
339static void adp5520_bl_remove(struct platform_device *pdev)
340{
341	struct backlight_device *bl = platform_get_drvdata(pdev);
342	struct adp5520_bl *data = bl_get_data(bl);
343
344	adp5520_clr_bits(data->master, ADP5520_MODE_STATUS, ADP5520_BL_EN);
345
346	if (data->pdata->en_ambl_sens)
347		sysfs_remove_group(&bl->dev.kobj,
348				&adp5520_bl_attr_group);
349}
350
351#ifdef CONFIG_PM_SLEEP
352static int adp5520_bl_suspend(struct device *dev)
353{
354	struct backlight_device *bl = dev_get_drvdata(dev);
355
356	return adp5520_bl_set(bl, 0);
357}
358
359static int adp5520_bl_resume(struct device *dev)
360{
361	struct backlight_device *bl = dev_get_drvdata(dev);
362
363	backlight_update_status(bl);
364	return 0;
365}
366#endif
367
368static SIMPLE_DEV_PM_OPS(adp5520_bl_pm_ops, adp5520_bl_suspend,
369			adp5520_bl_resume);
370
371static struct platform_driver adp5520_bl_driver = {
372	.driver		= {
373		.name	= "adp5520-backlight",
374		.pm	= &adp5520_bl_pm_ops,
375	},
376	.probe		= adp5520_bl_probe,
377	.remove		= adp5520_bl_remove,
378};
379
380module_platform_driver(adp5520_bl_driver);
381
382MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
383MODULE_DESCRIPTION("ADP5520(01) Backlight Driver");
384MODULE_LICENSE("GPL");
385MODULE_ALIAS("platform:adp5520-backlight");
~/ Read-only file preview.