brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · c7aefcd Raw
257 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2015-2019 Texas Instruments Incorporated -  http://www.ti.com/4 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>5 *6 * Based on pwm_bl.c7 */8 9#include <linux/backlight.h>10#include <linux/leds.h>11#include <linux/module.h>12#include <linux/platform_device.h>13 14struct led_bl_data {15	struct device		*dev;16	struct backlight_device	*bl_dev;17	struct led_classdev	**leds;18	bool			enabled;19	int			nb_leds;20	unsigned int		*levels;21	unsigned int		default_brightness;22	unsigned int		max_brightness;23};24 25static void led_bl_set_brightness(struct led_bl_data *priv, int level)26{27	int i;28	int bkl_brightness;29 30	if (priv->levels)31		bkl_brightness = priv->levels[level];32	else33		bkl_brightness = level;34 35	for (i = 0; i < priv->nb_leds; i++)36		led_set_brightness(priv->leds[i], bkl_brightness);37 38	priv->enabled = true;39}40 41static void led_bl_power_off(struct led_bl_data *priv)42{43	int i;44 45	if (!priv->enabled)46		return;47 48	for (i = 0; i < priv->nb_leds; i++)49		led_set_brightness(priv->leds[i], LED_OFF);50 51	priv->enabled = false;52}53 54static int led_bl_update_status(struct backlight_device *bl)55{56	struct led_bl_data *priv = bl_get_data(bl);57	int brightness = backlight_get_brightness(bl);58 59	if (brightness > 0)60		led_bl_set_brightness(priv, brightness);61	else62		led_bl_power_off(priv);63 64	return 0;65}66 67static const struct backlight_ops led_bl_ops = {68	.update_status	= led_bl_update_status,69};70 71static int led_bl_get_leds(struct device *dev,72			   struct led_bl_data *priv)73{74	int i, nb_leds, ret;75	struct device_node *node = dev->of_node;76	struct led_classdev **leds;77	unsigned int max_brightness;78	unsigned int default_brightness;79 80	ret = of_count_phandle_with_args(node, "leds", NULL);81	if (ret < 0) {82		dev_err(dev, "Unable to get led count\n");83		return -EINVAL;84	}85 86	nb_leds = ret;87	if (nb_leds < 1) {88		dev_err(dev, "At least one LED must be specified!\n");89		return -EINVAL;90	}91 92	leds = devm_kzalloc(dev, sizeof(struct led_classdev *) * nb_leds,93			    GFP_KERNEL);94	if (!leds)95		return -ENOMEM;96 97	for (i = 0; i < nb_leds; i++) {98		leds[i] = devm_of_led_get(dev, i);99		if (IS_ERR(leds[i]))100			return PTR_ERR(leds[i]);101	}102 103	/* check that the LEDs all have the same brightness range */104	max_brightness = leds[0]->max_brightness;105	for (i = 1; i < nb_leds; i++) {106		if (max_brightness != leds[i]->max_brightness) {107			dev_err(dev, "LEDs must have identical ranges\n");108			return -EINVAL;109		}110	}111 112	/* get the default brightness from the first LED from the list */113	default_brightness = leds[0]->brightness;114 115	priv->nb_leds = nb_leds;116	priv->leds = leds;117	priv->max_brightness = max_brightness;118	priv->default_brightness = default_brightness;119 120	return 0;121}122 123static int led_bl_parse_levels(struct device *dev,124			   struct led_bl_data *priv)125{126	struct device_node *node = dev->of_node;127	int num_levels;128	u32 value;129	int ret;130 131	if (!node)132		return -ENODEV;133 134	num_levels = of_property_count_u32_elems(node, "brightness-levels");135	if (num_levels > 1) {136		int i;137		unsigned int db;138		u32 *levels = NULL;139 140		levels = devm_kzalloc(dev, sizeof(u32) * num_levels,141				      GFP_KERNEL);142		if (!levels)143			return -ENOMEM;144 145		ret = of_property_read_u32_array(node, "brightness-levels",146						levels,147						num_levels);148		if (ret < 0)149			return ret;150 151		/*152		 * Try to map actual LED brightness to backlight brightness153		 * level154		 */155		db = priv->default_brightness;156		for (i = 0 ; i < num_levels; i++) {157			if ((i && db > levels[i-1]) && db <= levels[i])158				break;159		}160		priv->default_brightness = i;161		priv->max_brightness = num_levels - 1;162		priv->levels = levels;163	} else if (num_levels >= 0)164		dev_warn(dev, "Not enough levels defined\n");165 166	ret = of_property_read_u32(node, "default-brightness-level", &value);167	if (!ret && value <= priv->max_brightness)168		priv->default_brightness = value;169	else if (!ret  && value > priv->max_brightness)170		dev_warn(dev, "Invalid default brightness. Ignoring it\n");171 172	return 0;173}174 175static int led_bl_probe(struct platform_device *pdev)176{177	struct backlight_properties props;178	struct led_bl_data *priv;179	int ret, i;180 181	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);182	if (!priv)183		return -ENOMEM;184 185	platform_set_drvdata(pdev, priv);186 187	priv->dev = &pdev->dev;188 189	ret = led_bl_get_leds(&pdev->dev, priv);190	if (ret)191		return ret;192 193	ret = led_bl_parse_levels(&pdev->dev, priv);194	if (ret < 0) {195		dev_err(&pdev->dev, "Failed to parse DT data\n");196		return ret;197	}198 199	memset(&props, 0, sizeof(struct backlight_properties));200	props.type = BACKLIGHT_RAW;201	props.max_brightness = priv->max_brightness;202	props.brightness = priv->default_brightness;203	props.power = (priv->default_brightness > 0) ? BACKLIGHT_POWER_OFF :204		      BACKLIGHT_POWER_ON;205	priv->bl_dev = backlight_device_register(dev_name(&pdev->dev),206			&pdev->dev, priv, &led_bl_ops, &props);207	if (IS_ERR(priv->bl_dev)) {208		dev_err(&pdev->dev, "Failed to register backlight\n");209		return PTR_ERR(priv->bl_dev);210	}211 212	for (i = 0; i < priv->nb_leds; i++) {213		mutex_lock(&priv->leds[i]->led_access);214		led_sysfs_disable(priv->leds[i]);215		mutex_unlock(&priv->leds[i]->led_access);216	}217 218	backlight_update_status(priv->bl_dev);219 220	return 0;221}222 223static void led_bl_remove(struct platform_device *pdev)224{225	struct led_bl_data *priv = platform_get_drvdata(pdev);226	struct backlight_device *bl = priv->bl_dev;227	int i;228 229	backlight_device_unregister(bl);230 231	led_bl_power_off(priv);232	for (i = 0; i < priv->nb_leds; i++)233		led_sysfs_enable(priv->leds[i]);234}235 236static const struct of_device_id led_bl_of_match[] = {237	{ .compatible = "led-backlight" },238	{ }239};240 241MODULE_DEVICE_TABLE(of, led_bl_of_match);242 243static struct platform_driver led_bl_driver = {244	.driver		= {245		.name		= "led-backlight",246		.of_match_table	= led_bl_of_match,247	},248	.probe		= led_bl_probe,249	.remove_new	= led_bl_remove,250};251 252module_platform_driver(led_bl_driver);253 254MODULE_DESCRIPTION("LED based Backlight Driver");255MODULE_LICENSE("GPL");256MODULE_ALIAS("platform:led-backlight");257