brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · 935043b Raw
348 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * sky81452-backlight.c	SKY81452 backlight driver4 *5 * Copyright 2014 Skyworks Solutions Inc.6 * Author : Gyungoh Yoo <jack.yoo@skyworksinc.com>7 */8 9#include <linux/backlight.h>10#include <linux/err.h>11#include <linux/gpio/consumer.h>12#include <linux/init.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/regmap.h>18#include <linux/slab.h>19 20/* registers */21#define SKY81452_REG0	0x0022#define SKY81452_REG1	0x0123#define SKY81452_REG2	0x0224#define SKY81452_REG4	0x0425#define SKY81452_REG5	0x0526 27/* bit mask */28#define SKY81452_CS	0xFF29#define SKY81452_EN	0x3F30#define SKY81452_IGPW	0x2031#define SKY81452_PWMMD	0x1032#define SKY81452_PHASE	0x0833#define SKY81452_ILIM	0x0434#define SKY81452_VSHRT	0x0335#define SKY81452_OCP	0x8036#define SKY81452_OTMP	0x4037#define SKY81452_SHRT	0x3F38#define SKY81452_OPN	0x3F39 40#define SKY81452_DEFAULT_NAME "lcd-backlight"41#define SKY81452_MAX_BRIGHTNESS	(SKY81452_CS + 1)42 43/**44 * struct sky81452_bl_platform_data - backlight platform data45 * @name:	backlight driver name.46 *		If it is not defined, default name is lcd-backlight.47 * @gpiod_enable:GPIO descriptor which control EN pin48 * @enable:	Enable mask for current sink channel 1, 2, 3, 4, 5 and 6.49 * @ignore_pwm:	true if DPWMI should be ignored.50 * @dpwm_mode:	true is DPWM dimming mode, otherwise Analog dimming mode.51 * @phase_shift:true is phase shift mode.52 * @short_detection_threshold:	It should be one of 4, 5, 6 and 7V.53 * @boost_current_limit:	It should be one of 2300, 2750mA.54 */55struct sky81452_bl_platform_data {56	const char *name;57	struct gpio_desc *gpiod_enable;58	unsigned int enable;59	bool ignore_pwm;60	bool dpwm_mode;61	bool phase_shift;62	unsigned int short_detection_threshold;63	unsigned int boost_current_limit;64};65 66#define CTZ(b) __builtin_ctz(b)67 68static int sky81452_bl_update_status(struct backlight_device *bd)69{70	const struct sky81452_bl_platform_data *pdata =71			dev_get_platdata(bd->dev.parent);72	const unsigned int brightness = (unsigned int)bd->props.brightness;73	struct regmap *regmap = bl_get_data(bd);74	int ret;75 76	if (brightness > 0) {77		ret = regmap_write(regmap, SKY81452_REG0, brightness - 1);78		if (ret < 0)79			return ret;80 81		return regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN,82					pdata->enable << CTZ(SKY81452_EN));83	}84 85	return regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN, 0);86}87 88static const struct backlight_ops sky81452_bl_ops = {89	.update_status = sky81452_bl_update_status,90};91 92static ssize_t sky81452_bl_store_enable(struct device *dev,93		struct device_attribute *attr, const char *buf, size_t count)94{95	struct regmap *regmap = bl_get_data(to_backlight_device(dev));96	unsigned long value;97	int ret;98 99	ret = kstrtoul(buf, 16, &value);100	if (ret < 0)101		return ret;102 103	ret = regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN,104					value << CTZ(SKY81452_EN));105	if (ret < 0)106		return ret;107 108	return count;109}110 111static ssize_t sky81452_bl_show_open_short(struct device *dev,112		struct device_attribute *attr, char *buf)113{114	struct regmap *regmap = bl_get_data(to_backlight_device(dev));115	unsigned int reg, value = 0;116	char tmp[3];117	int i, ret;118 119	reg = !strcmp(attr->attr.name, "open") ? SKY81452_REG5 : SKY81452_REG4;120	ret = regmap_read(regmap, reg, &value);121	if (ret < 0)122		return ret;123 124	if (value & SKY81452_SHRT) {125		*buf = 0;126		for (i = 0; i < 6; i++) {127			if (value & 0x01) {128				sprintf(tmp, "%d ", i + 1);129				strcat(buf, tmp);130			}131			value >>= 1;132		}133		strcat(buf, "\n");134	} else {135		strcpy(buf, "none\n");136	}137 138	return strlen(buf);139}140 141static ssize_t sky81452_bl_show_fault(struct device *dev,142		struct device_attribute *attr, char *buf)143{144	struct regmap *regmap = bl_get_data(to_backlight_device(dev));145	unsigned int value = 0;146	int ret;147 148	ret = regmap_read(regmap, SKY81452_REG4, &value);149	if (ret < 0)150		return ret;151 152	*buf = 0;153 154	if (value & SKY81452_OCP)155		strcat(buf, "over-current ");156 157	if (value & SKY81452_OTMP)158		strcat(buf, "over-temperature");159 160	strcat(buf, "\n");161	return strlen(buf);162}163 164static DEVICE_ATTR(enable, S_IWGRP | S_IWUSR, NULL, sky81452_bl_store_enable);165static DEVICE_ATTR(open, S_IRUGO, sky81452_bl_show_open_short, NULL);166static DEVICE_ATTR(short, S_IRUGO, sky81452_bl_show_open_short, NULL);167static DEVICE_ATTR(fault, S_IRUGO, sky81452_bl_show_fault, NULL);168 169static struct attribute *sky81452_bl_attribute[] = {170	&dev_attr_enable.attr,171	&dev_attr_open.attr,172	&dev_attr_short.attr,173	&dev_attr_fault.attr,174	NULL175};176 177static const struct attribute_group sky81452_bl_attr_group = {178	.attrs = sky81452_bl_attribute,179};180 181#ifdef CONFIG_OF182static struct sky81452_bl_platform_data *sky81452_bl_parse_dt(183							struct device *dev)184{185	struct device_node *np = dev->of_node;186	struct sky81452_bl_platform_data *pdata;187	int num_entry;188	unsigned int sources[6];189	int ret;190 191	if (!np) {192		dev_err(dev, "backlight node not found.\n");193		return ERR_PTR(-ENODATA);194	}195 196	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);197	if (!pdata)198		return ERR_PTR(-ENOMEM);199 200	of_property_read_string(np, "name", &pdata->name);201	pdata->ignore_pwm = of_property_read_bool(np, "skyworks,ignore-pwm");202	pdata->dpwm_mode = of_property_read_bool(np, "skyworks,dpwm-mode");203	pdata->phase_shift = of_property_read_bool(np, "skyworks,phase-shift");204	pdata->gpiod_enable = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);205 206	ret = of_property_count_u32_elems(np, "led-sources");207	if (ret < 0) {208		pdata->enable = SKY81452_EN >> CTZ(SKY81452_EN);209	} else {210		num_entry = ret;211		if (num_entry > 6)212			num_entry = 6;213 214		ret = of_property_read_u32_array(np, "led-sources", sources,215					num_entry);216		if (ret < 0) {217			dev_err(dev, "led-sources node is invalid.\n");218			return ERR_PTR(-EINVAL);219		}220 221		pdata->enable = 0;222		while (--num_entry)223			pdata->enable |= (1 << sources[num_entry]);224	}225 226	ret = of_property_read_u32(np,227			"skyworks,short-detection-threshold-volt",228			&pdata->short_detection_threshold);229	if (ret < 0)230		pdata->short_detection_threshold = 7;231 232	ret = of_property_read_u32(np, "skyworks,current-limit-mA",233			&pdata->boost_current_limit);234	if (ret < 0)235		pdata->boost_current_limit = 2750;236 237	return pdata;238}239#else240static struct sky81452_bl_platform_data *sky81452_bl_parse_dt(241							struct device *dev)242{243	return ERR_PTR(-EINVAL);244}245#endif246 247static int sky81452_bl_init_device(struct regmap *regmap,248		struct sky81452_bl_platform_data *pdata)249{250	unsigned int value;251 252	value = pdata->ignore_pwm ? SKY81452_IGPW : 0;253	value |= pdata->dpwm_mode ? SKY81452_PWMMD : 0;254	value |= pdata->phase_shift ? 0 : SKY81452_PHASE;255 256	if (pdata->boost_current_limit == 2300)257		value |= SKY81452_ILIM;258	else if (pdata->boost_current_limit != 2750)259		return -EINVAL;260 261	if (pdata->short_detection_threshold < 4 ||262				pdata->short_detection_threshold > 7)263		return -EINVAL;264	value |= (7 - pdata->short_detection_threshold) << CTZ(SKY81452_VSHRT);265 266	return regmap_write(regmap, SKY81452_REG2, value);267}268 269static int sky81452_bl_probe(struct platform_device *pdev)270{271	struct device *dev = &pdev->dev;272	struct regmap *regmap = dev_get_drvdata(dev->parent);273	struct sky81452_bl_platform_data *pdata;274	struct backlight_device *bd;275	struct backlight_properties props;276	const char *name;277	int ret;278 279	pdata = sky81452_bl_parse_dt(dev);280	if (IS_ERR(pdata))281		return PTR_ERR(pdata);282 283	ret = sky81452_bl_init_device(regmap, pdata);284	if (ret < 0) {285		dev_err(dev, "failed to initialize. err=%d\n", ret);286		return ret;287	}288 289	memset(&props, 0, sizeof(props));290	props.max_brightness = SKY81452_MAX_BRIGHTNESS;291	name = pdata->name ? pdata->name : SKY81452_DEFAULT_NAME;292	bd = devm_backlight_device_register(dev, name, dev, regmap,293						&sky81452_bl_ops, &props);294	if (IS_ERR(bd)) {295		dev_err(dev, "failed to register. err=%ld\n", PTR_ERR(bd));296		return PTR_ERR(bd);297	}298 299	platform_set_drvdata(pdev, bd);300 301	ret = sysfs_create_group(&bd->dev.kobj, &sky81452_bl_attr_group);302	if (ret < 0) {303		dev_err(dev, "failed to create attribute. err=%d\n", ret);304		return ret;305	}306 307	return ret;308}309 310static void sky81452_bl_remove(struct platform_device *pdev)311{312	const struct sky81452_bl_platform_data *pdata =313						dev_get_platdata(&pdev->dev);314	struct backlight_device *bd = platform_get_drvdata(pdev);315 316	sysfs_remove_group(&bd->dev.kobj, &sky81452_bl_attr_group);317 318	bd->props.power = BACKLIGHT_POWER_ON;319	bd->props.brightness = 0;320	backlight_update_status(bd);321 322	if (pdata->gpiod_enable)323		gpiod_set_value_cansleep(pdata->gpiod_enable, 0);324}325 326#ifdef CONFIG_OF327static const struct of_device_id sky81452_bl_of_match[] = {328	{ .compatible = "skyworks,sky81452-backlight", },329	{ }330};331MODULE_DEVICE_TABLE(of, sky81452_bl_of_match);332#endif333 334static struct platform_driver sky81452_bl_driver = {335	.driver = {336		.name = "sky81452-backlight",337		.of_match_table = of_match_ptr(sky81452_bl_of_match),338	},339	.probe = sky81452_bl_probe,340	.remove_new = sky81452_bl_remove,341};342 343module_platform_driver(sky81452_bl_driver);344 345MODULE_DESCRIPTION("Skyworks SKY81452 backlight driver");346MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>");347MODULE_LICENSE("GPL v2");348