brintos

brintos / linux-shallow public Read only

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