cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

led_bl.c (5876B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      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.c
      7 */
      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	else
     33		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	else
     62		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 brightness
    153		 * level
    154		 */
    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) ? FB_BLANK_POWERDOWN :
    204		      FB_BLANK_UNBLANK;
    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		led_sysfs_disable(priv->leds[i]);
    214
    215	backlight_update_status(priv->bl_dev);
    216
    217	return 0;
    218}
    219
    220static int led_bl_remove(struct platform_device *pdev)
    221{
    222	struct led_bl_data *priv = platform_get_drvdata(pdev);
    223	struct backlight_device *bl = priv->bl_dev;
    224	int i;
    225
    226	backlight_device_unregister(bl);
    227
    228	led_bl_power_off(priv);
    229	for (i = 0; i < priv->nb_leds; i++)
    230		led_sysfs_enable(priv->leds[i]);
    231
    232	return 0;
    233}
    234
    235static const struct of_device_id led_bl_of_match[] = {
    236	{ .compatible = "led-backlight" },
    237	{ }
    238};
    239
    240MODULE_DEVICE_TABLE(of, led_bl_of_match);
    241
    242static struct platform_driver led_bl_driver = {
    243	.driver		= {
    244		.name		= "led-backlight",
    245		.of_match_table	= of_match_ptr(led_bl_of_match),
    246	},
    247	.probe		= led_bl_probe,
    248	.remove		= led_bl_remove,
    249};
    250
    251module_platform_driver(led_bl_driver);
    252
    253MODULE_DESCRIPTION("LED based Backlight Driver");
    254MODULE_LICENSE("GPL");
    255MODULE_ALIAS("platform:led-backlight");