Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2015-2019 Texas Instruments Incorporated -  http://www.ti.com/
0004  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
0005  *
0006  * Based on pwm_bl.c
0007  */
0008 
0009 #include <linux/backlight.h>
0010 #include <linux/leds.h>
0011 #include <linux/module.h>
0012 #include <linux/platform_device.h>
0013 
0014 struct led_bl_data {
0015     struct device       *dev;
0016     struct backlight_device *bl_dev;
0017     struct led_classdev **leds;
0018     bool            enabled;
0019     int         nb_leds;
0020     unsigned int        *levels;
0021     unsigned int        default_brightness;
0022     unsigned int        max_brightness;
0023 };
0024 
0025 static void led_bl_set_brightness(struct led_bl_data *priv, int level)
0026 {
0027     int i;
0028     int bkl_brightness;
0029 
0030     if (priv->levels)
0031         bkl_brightness = priv->levels[level];
0032     else
0033         bkl_brightness = level;
0034 
0035     for (i = 0; i < priv->nb_leds; i++)
0036         led_set_brightness(priv->leds[i], bkl_brightness);
0037 
0038     priv->enabled = true;
0039 }
0040 
0041 static void led_bl_power_off(struct led_bl_data *priv)
0042 {
0043     int i;
0044 
0045     if (!priv->enabled)
0046         return;
0047 
0048     for (i = 0; i < priv->nb_leds; i++)
0049         led_set_brightness(priv->leds[i], LED_OFF);
0050 
0051     priv->enabled = false;
0052 }
0053 
0054 static int led_bl_update_status(struct backlight_device *bl)
0055 {
0056     struct led_bl_data *priv = bl_get_data(bl);
0057     int brightness = backlight_get_brightness(bl);
0058 
0059     if (brightness > 0)
0060         led_bl_set_brightness(priv, brightness);
0061     else
0062         led_bl_power_off(priv);
0063 
0064     return 0;
0065 }
0066 
0067 static const struct backlight_ops led_bl_ops = {
0068     .update_status  = led_bl_update_status,
0069 };
0070 
0071 static int led_bl_get_leds(struct device *dev,
0072                struct led_bl_data *priv)
0073 {
0074     int i, nb_leds, ret;
0075     struct device_node *node = dev->of_node;
0076     struct led_classdev **leds;
0077     unsigned int max_brightness;
0078     unsigned int default_brightness;
0079 
0080     ret = of_count_phandle_with_args(node, "leds", NULL);
0081     if (ret < 0) {
0082         dev_err(dev, "Unable to get led count\n");
0083         return -EINVAL;
0084     }
0085 
0086     nb_leds = ret;
0087     if (nb_leds < 1) {
0088         dev_err(dev, "At least one LED must be specified!\n");
0089         return -EINVAL;
0090     }
0091 
0092     leds = devm_kzalloc(dev, sizeof(struct led_classdev *) * nb_leds,
0093                 GFP_KERNEL);
0094     if (!leds)
0095         return -ENOMEM;
0096 
0097     for (i = 0; i < nb_leds; i++) {
0098         leds[i] = devm_of_led_get(dev, i);
0099         if (IS_ERR(leds[i]))
0100             return PTR_ERR(leds[i]);
0101     }
0102 
0103     /* check that the LEDs all have the same brightness range */
0104     max_brightness = leds[0]->max_brightness;
0105     for (i = 1; i < nb_leds; i++) {
0106         if (max_brightness != leds[i]->max_brightness) {
0107             dev_err(dev, "LEDs must have identical ranges\n");
0108             return -EINVAL;
0109         }
0110     }
0111 
0112     /* get the default brightness from the first LED from the list */
0113     default_brightness = leds[0]->brightness;
0114 
0115     priv->nb_leds = nb_leds;
0116     priv->leds = leds;
0117     priv->max_brightness = max_brightness;
0118     priv->default_brightness = default_brightness;
0119 
0120     return 0;
0121 }
0122 
0123 static int led_bl_parse_levels(struct device *dev,
0124                struct led_bl_data *priv)
0125 {
0126     struct device_node *node = dev->of_node;
0127     int num_levels;
0128     u32 value;
0129     int ret;
0130 
0131     if (!node)
0132         return -ENODEV;
0133 
0134     num_levels = of_property_count_u32_elems(node, "brightness-levels");
0135     if (num_levels > 1) {
0136         int i;
0137         unsigned int db;
0138         u32 *levels = NULL;
0139 
0140         levels = devm_kzalloc(dev, sizeof(u32) * num_levels,
0141                       GFP_KERNEL);
0142         if (!levels)
0143             return -ENOMEM;
0144 
0145         ret = of_property_read_u32_array(node, "brightness-levels",
0146                         levels,
0147                         num_levels);
0148         if (ret < 0)
0149             return ret;
0150 
0151         /*
0152          * Try to map actual LED brightness to backlight brightness
0153          * level
0154          */
0155         db = priv->default_brightness;
0156         for (i = 0 ; i < num_levels; i++) {
0157             if ((i && db > levels[i-1]) && db <= levels[i])
0158                 break;
0159         }
0160         priv->default_brightness = i;
0161         priv->max_brightness = num_levels - 1;
0162         priv->levels = levels;
0163     } else if (num_levels >= 0)
0164         dev_warn(dev, "Not enough levels defined\n");
0165 
0166     ret = of_property_read_u32(node, "default-brightness-level", &value);
0167     if (!ret && value <= priv->max_brightness)
0168         priv->default_brightness = value;
0169     else if (!ret  && value > priv->max_brightness)
0170         dev_warn(dev, "Invalid default brightness. Ignoring it\n");
0171 
0172     return 0;
0173 }
0174 
0175 static int led_bl_probe(struct platform_device *pdev)
0176 {
0177     struct backlight_properties props;
0178     struct led_bl_data *priv;
0179     int ret, i;
0180 
0181     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0182     if (!priv)
0183         return -ENOMEM;
0184 
0185     platform_set_drvdata(pdev, priv);
0186 
0187     priv->dev = &pdev->dev;
0188 
0189     ret = led_bl_get_leds(&pdev->dev, priv);
0190     if (ret)
0191         return ret;
0192 
0193     ret = led_bl_parse_levels(&pdev->dev, priv);
0194     if (ret < 0) {
0195         dev_err(&pdev->dev, "Failed to parse DT data\n");
0196         return ret;
0197     }
0198 
0199     memset(&props, 0, sizeof(struct backlight_properties));
0200     props.type = BACKLIGHT_RAW;
0201     props.max_brightness = priv->max_brightness;
0202     props.brightness = priv->default_brightness;
0203     props.power = (priv->default_brightness > 0) ? FB_BLANK_POWERDOWN :
0204               FB_BLANK_UNBLANK;
0205     priv->bl_dev = backlight_device_register(dev_name(&pdev->dev),
0206             &pdev->dev, priv, &led_bl_ops, &props);
0207     if (IS_ERR(priv->bl_dev)) {
0208         dev_err(&pdev->dev, "Failed to register backlight\n");
0209         return PTR_ERR(priv->bl_dev);
0210     }
0211 
0212     for (i = 0; i < priv->nb_leds; i++)
0213         led_sysfs_disable(priv->leds[i]);
0214 
0215     backlight_update_status(priv->bl_dev);
0216 
0217     return 0;
0218 }
0219 
0220 static int led_bl_remove(struct platform_device *pdev)
0221 {
0222     struct led_bl_data *priv = platform_get_drvdata(pdev);
0223     struct backlight_device *bl = priv->bl_dev;
0224     int i;
0225 
0226     backlight_device_unregister(bl);
0227 
0228     led_bl_power_off(priv);
0229     for (i = 0; i < priv->nb_leds; i++)
0230         led_sysfs_enable(priv->leds[i]);
0231 
0232     return 0;
0233 }
0234 
0235 static const struct of_device_id led_bl_of_match[] = {
0236     { .compatible = "led-backlight" },
0237     { }
0238 };
0239 
0240 MODULE_DEVICE_TABLE(of, led_bl_of_match);
0241 
0242 static struct platform_driver led_bl_driver = {
0243     .driver     = {
0244         .name       = "led-backlight",
0245         .of_match_table = of_match_ptr(led_bl_of_match),
0246     },
0247     .probe      = led_bl_probe,
0248     .remove     = led_bl_remove,
0249 };
0250 
0251 module_platform_driver(led_bl_driver);
0252 
0253 MODULE_DESCRIPTION("LED based Backlight Driver");
0254 MODULE_LICENSE("GPL");
0255 MODULE_ALIAS("platform:led-backlight");