0001
0002
0003
0004
0005
0006
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
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
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
0153
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");