1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
4 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
9 #include <linux/backlight.h>
10 #include <linux/leds.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
16 struct backlight_device
*bl_dev
;
17 struct led_classdev
**leds
;
21 unsigned int default_brightness
;
22 unsigned int max_brightness
;
25 static void led_bl_set_brightness(struct led_bl_data
*priv
, int level
)
31 bkl_brightness
= priv
->levels
[level
];
33 bkl_brightness
= level
;
35 for (i
= 0; i
< priv
->nb_leds
; i
++)
36 led_set_brightness(priv
->leds
[i
], bkl_brightness
);
41 static void led_bl_power_off(struct led_bl_data
*priv
)
48 for (i
= 0; i
< priv
->nb_leds
; i
++)
49 led_set_brightness(priv
->leds
[i
], LED_OFF
);
51 priv
->enabled
= false;
54 static int led_bl_update_status(struct backlight_device
*bl
)
56 struct led_bl_data
*priv
= bl_get_data(bl
);
57 int brightness
= backlight_get_brightness(bl
);
60 led_bl_set_brightness(priv
, brightness
);
62 led_bl_power_off(priv
);
67 static const struct backlight_ops led_bl_ops
= {
68 .update_status
= led_bl_update_status
,
71 static int led_bl_get_leds(struct device
*dev
,
72 struct led_bl_data
*priv
)
75 struct device_node
*node
= dev
->of_node
;
76 struct led_classdev
**leds
;
77 unsigned int max_brightness
;
78 unsigned int default_brightness
;
80 ret
= of_count_phandle_with_args(node
, "leds", NULL
);
82 dev_err(dev
, "Unable to get led count\n");
88 dev_err(dev
, "At least one LED must be specified!\n");
92 leds
= devm_kzalloc(dev
, sizeof(struct led_classdev
*) * nb_leds
,
97 for (i
= 0; i
< nb_leds
; i
++) {
98 leds
[i
] = devm_of_led_get(dev
, i
);
100 return PTR_ERR(leds
[i
]);
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");
112 /* get the default brightness from the first LED from the list */
113 default_brightness
= leds
[0]->brightness
;
115 priv
->nb_leds
= nb_leds
;
117 priv
->max_brightness
= max_brightness
;
118 priv
->default_brightness
= default_brightness
;
123 static int led_bl_parse_levels(struct device
*dev
,
124 struct led_bl_data
*priv
)
126 struct device_node
*node
= dev
->of_node
;
134 num_levels
= of_property_count_u32_elems(node
, "brightness-levels");
135 if (num_levels
> 1) {
140 levels
= devm_kzalloc(dev
, sizeof(u32
) * num_levels
,
145 ret
= of_property_read_u32_array(node
, "brightness-levels",
152 * Try to map actual LED brightness to backlight brightness
155 db
= priv
->default_brightness
;
156 for (i
= 0 ; i
< num_levels
; i
++) {
157 if ((i
&& db
> levels
[i
-1]) && db
<= levels
[i
])
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");
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");
175 static int led_bl_probe(struct platform_device
*pdev
)
177 struct backlight_properties props
;
178 struct led_bl_data
*priv
;
181 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
185 platform_set_drvdata(pdev
, priv
);
187 priv
->dev
= &pdev
->dev
;
189 ret
= led_bl_get_leds(&pdev
->dev
, priv
);
193 ret
= led_bl_parse_levels(&pdev
->dev
, priv
);
195 dev_err(&pdev
->dev
, "Failed to parse DT data\n");
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
:
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
);
212 for (i
= 0; i
< priv
->nb_leds
; i
++)
213 led_sysfs_disable(priv
->leds
[i
]);
215 backlight_update_status(priv
->bl_dev
);
220 static int led_bl_remove(struct platform_device
*pdev
)
222 struct led_bl_data
*priv
= platform_get_drvdata(pdev
);
223 struct backlight_device
*bl
= priv
->bl_dev
;
226 backlight_device_unregister(bl
);
228 led_bl_power_off(priv
);
229 for (i
= 0; i
< priv
->nb_leds
; i
++)
230 led_sysfs_enable(priv
->leds
[i
]);
235 static const struct of_device_id led_bl_of_match
[] = {
236 { .compatible
= "led-backlight" },
240 MODULE_DEVICE_TABLE(of
, led_bl_of_match
);
242 static struct platform_driver led_bl_driver
= {
244 .name
= "led-backlight",
245 .of_match_table
= of_match_ptr(led_bl_of_match
),
247 .probe
= led_bl_probe
,
248 .remove
= led_bl_remove
,
251 module_platform_driver(led_bl_driver
);
253 MODULE_DESCRIPTION("LED based Backlight Driver");
254 MODULE_LICENSE("GPL");
255 MODULE_ALIAS("platform:led-backlight");