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
= bl
->props
.brightness
;
59 if (bl
->props
.power
!= FB_BLANK_UNBLANK
||
60 bl
->props
.fb_blank
!= FB_BLANK_UNBLANK
||
61 bl
->props
.state
& BL_CORE_FBBLANK
)
65 led_bl_set_brightness(priv
, brightness
);
67 led_bl_power_off(priv
);
72 static const struct backlight_ops led_bl_ops
= {
73 .update_status
= led_bl_update_status
,
76 static int led_bl_get_leds(struct device
*dev
,
77 struct led_bl_data
*priv
)
80 struct device_node
*node
= dev
->of_node
;
81 struct led_classdev
**leds
;
82 unsigned int max_brightness
;
83 unsigned int default_brightness
;
85 ret
= of_count_phandle_with_args(node
, "leds", NULL
);
87 dev_err(dev
, "Unable to get led count\n");
93 dev_err(dev
, "At least one LED must be specified!\n");
97 leds
= devm_kzalloc(dev
, sizeof(struct led_classdev
*) * nb_leds
,
102 for (i
= 0; i
< nb_leds
; i
++) {
103 leds
[i
] = devm_of_led_get(dev
, i
);
105 return PTR_ERR(leds
[i
]);
108 /* check that the LEDs all have the same brightness range */
109 max_brightness
= leds
[0]->max_brightness
;
110 for (i
= 1; i
< nb_leds
; i
++) {
111 if (max_brightness
!= leds
[i
]->max_brightness
) {
112 dev_err(dev
, "LEDs must have identical ranges\n");
117 /* get the default brightness from the first LED from the list */
118 default_brightness
= leds
[0]->brightness
;
120 priv
->nb_leds
= nb_leds
;
122 priv
->max_brightness
= max_brightness
;
123 priv
->default_brightness
= default_brightness
;
128 static int led_bl_parse_levels(struct device
*dev
,
129 struct led_bl_data
*priv
)
131 struct device_node
*node
= dev
->of_node
;
139 num_levels
= of_property_count_u32_elems(node
, "brightness-levels");
140 if (num_levels
> 1) {
145 levels
= devm_kzalloc(dev
, sizeof(u32
) * num_levels
,
150 ret
= of_property_read_u32_array(node
, "brightness-levels",
157 * Try to map actual LED brightness to backlight brightness
160 db
= priv
->default_brightness
;
161 for (i
= 0 ; i
< num_levels
; i
++) {
162 if ((i
&& db
> levels
[i
-1]) && db
<= levels
[i
])
165 priv
->default_brightness
= i
;
166 priv
->max_brightness
= num_levels
- 1;
167 priv
->levels
= levels
;
168 } else if (num_levels
>= 0)
169 dev_warn(dev
, "Not enough levels defined\n");
171 ret
= of_property_read_u32(node
, "default-brightness-level", &value
);
172 if (!ret
&& value
<= priv
->max_brightness
)
173 priv
->default_brightness
= value
;
174 else if (!ret
&& value
> priv
->max_brightness
)
175 dev_warn(dev
, "Invalid default brightness. Ignoring it\n");
180 static int led_bl_probe(struct platform_device
*pdev
)
182 struct backlight_properties props
;
183 struct led_bl_data
*priv
;
186 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
190 platform_set_drvdata(pdev
, priv
);
192 priv
->dev
= &pdev
->dev
;
194 ret
= led_bl_get_leds(&pdev
->dev
, priv
);
198 ret
= led_bl_parse_levels(&pdev
->dev
, priv
);
200 dev_err(&pdev
->dev
, "Failed to parse DT data\n");
204 memset(&props
, 0, sizeof(struct backlight_properties
));
205 props
.type
= BACKLIGHT_RAW
;
206 props
.max_brightness
= priv
->max_brightness
;
207 props
.brightness
= priv
->default_brightness
;
208 props
.power
= (priv
->default_brightness
> 0) ? FB_BLANK_POWERDOWN
:
210 priv
->bl_dev
= backlight_device_register(dev_name(&pdev
->dev
),
211 &pdev
->dev
, priv
, &led_bl_ops
, &props
);
212 if (IS_ERR(priv
->bl_dev
)) {
213 dev_err(&pdev
->dev
, "Failed to register backlight\n");
214 return PTR_ERR(priv
->bl_dev
);
217 for (i
= 0; i
< priv
->nb_leds
; i
++)
218 led_sysfs_disable(priv
->leds
[i
]);
220 backlight_update_status(priv
->bl_dev
);
225 static int led_bl_remove(struct platform_device
*pdev
)
227 struct led_bl_data
*priv
= platform_get_drvdata(pdev
);
228 struct backlight_device
*bl
= priv
->bl_dev
;
231 backlight_device_unregister(bl
);
233 led_bl_power_off(priv
);
234 for (i
= 0; i
< priv
->nb_leds
; i
++)
235 led_sysfs_enable(priv
->leds
[i
]);
240 static const struct of_device_id led_bl_of_match
[] = {
241 { .compatible
= "led-backlight" },
245 MODULE_DEVICE_TABLE(of
, led_bl_of_match
);
247 static struct platform_driver led_bl_driver
= {
249 .name
= "led-backlight",
250 .of_match_table
= of_match_ptr(led_bl_of_match
),
252 .probe
= led_bl_probe
,
253 .remove
= led_bl_remove
,
256 module_platform_driver(led_bl_driver
);
258 MODULE_DESCRIPTION("LED based Backlight Driver");
259 MODULE_LICENSE("GPL");
260 MODULE_ALIAS("platform:led-backlight");