Skip to content

Commit 88ba95b

Browse files
Enric Balletbo i SerraLee Jones
authored andcommitted
backlight: pwm_bl: Compute brightness of LED linearly to human eye
When you want to change the brightness using a PWM signal, one thing you need to consider is how human perceive the brightness. Human perceive the brightness change non-linearly, we have better sensitivity at low luminance than high luminance, so to achieve perceived linear dimming, the brightness must be matches to the way our eyes behave. The CIE 1931 lightness formula is what actually describes how we perceive light. This patch computes a default table with the brightness levels filled with the numbers provided by the CIE 1931 algorithm, the number of the brightness levels is calculated based on the PWM resolution. The calculation of the table using the CIE 1931 algorithm is enabled by default when you do not define the 'brightness-levels' propriety in your device tree. Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com> Acked-by: Daniel Thompson <daniel.thompson@linaro.org> Signed-off-by: Lee Jones <lee.jones@linaro.org>
1 parent 1e5e7cc commit 88ba95b

File tree

1 file changed

+136
-13
lines changed

1 file changed

+136
-13
lines changed

drivers/video/backlight/pwm_bl.c

Lines changed: 136 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,107 @@ static const struct backlight_ops pwm_backlight_ops = {
143143
};
144144

145145
#ifdef CONFIG_OF
146+
#define PWM_LUMINANCE_SCALE 10000 /* luminance scale */
147+
148+
/* An integer based power function */
149+
static u64 int_pow(u64 base, int exp)
150+
{
151+
u64 result = 1;
152+
153+
while (exp) {
154+
if (exp & 1)
155+
result *= base;
156+
exp >>= 1;
157+
base *= base;
158+
}
159+
160+
return result;
161+
}
162+
163+
/*
164+
* CIE lightness to PWM conversion.
165+
*
166+
* The CIE 1931 lightness formula is what actually describes how we perceive
167+
* light:
168+
* Y = (L* / 902.3) if L* ≤ 0.08856
169+
* Y = ((L* + 16) / 116)^3 if L* > 0.08856
170+
*
171+
* Where Y is the luminance, the amount of light coming out of the screen, and
172+
* is a number between 0.0 and 1.0; and L* is the lightness, how bright a human
173+
* perceives the screen to be, and is a number between 0 and 100.
174+
*
175+
* The following function does the fixed point maths needed to implement the
176+
* above formula.
177+
*/
178+
static u64 cie1931(unsigned int lightness, unsigned int scale)
179+
{
180+
u64 retval;
181+
182+
lightness *= 100;
183+
if (lightness <= (8 * scale)) {
184+
retval = DIV_ROUND_CLOSEST_ULL(lightness * 10, 9023);
185+
} else {
186+
retval = int_pow((lightness + (16 * scale)) / 116, 3);
187+
retval = DIV_ROUND_CLOSEST_ULL(retval, (scale * scale));
188+
}
189+
190+
return retval;
191+
}
192+
193+
/*
194+
* Create a default correction table for PWM values to create linear brightness
195+
* for LED based backlights using the CIE1931 algorithm.
196+
*/
197+
static
198+
int pwm_backlight_brightness_default(struct device *dev,
199+
struct platform_pwm_backlight_data *data,
200+
unsigned int period)
201+
{
202+
unsigned int counter = 0;
203+
unsigned int i, n;
204+
u64 retval;
205+
206+
/*
207+
* Count the number of bits needed to represent the period number. The
208+
* number of bits is used to calculate the number of levels used for the
209+
* brightness-levels table, the purpose of this calculation is have a
210+
* pre-computed table with enough levels to get linear brightness
211+
* perception. The period is divided by the number of bits so for a
212+
* 8-bit PWM we have 255 / 8 = 32 brightness levels or for a 16-bit PWM
213+
* we have 65535 / 16 = 4096 brightness levels.
214+
*
215+
* Note that this method is based on empirical testing on different
216+
* devices with PWM of 8 and 16 bits of resolution.
217+
*/
218+
n = period;
219+
while (n) {
220+
counter += n % 2;
221+
n >>= 1;
222+
}
223+
224+
data->max_brightness = DIV_ROUND_UP(period, counter);
225+
data->levels = devm_kcalloc(dev, data->max_brightness,
226+
sizeof(*data->levels), GFP_KERNEL);
227+
if (!data->levels)
228+
return -ENOMEM;
229+
230+
/* Fill the table using the cie1931 algorithm */
231+
for (i = 0; i < data->max_brightness; i++) {
232+
retval = cie1931((i * PWM_LUMINANCE_SCALE) /
233+
data->max_brightness, PWM_LUMINANCE_SCALE) *
234+
period;
235+
retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE);
236+
if (retval > UINT_MAX)
237+
return -EINVAL;
238+
data->levels[i] = (unsigned int)retval;
239+
}
240+
241+
data->dft_brightness = data->max_brightness / 2;
242+
data->max_brightness--;
243+
244+
return 0;
245+
}
246+
146247
static int pwm_backlight_parse_dt(struct device *dev,
147248
struct platform_pwm_backlight_data *data)
148249
{
@@ -161,10 +262,13 @@ static int pwm_backlight_parse_dt(struct device *dev,
161262

162263
memset(data, 0, sizeof(*data));
163264

164-
/* determine the number of brightness levels */
265+
/*
266+
* Determine the number of brightness levels, if this property is not
267+
* set a default table of brightness levels will be used.
268+
*/
165269
prop = of_find_property(node, "brightness-levels", &length);
166270
if (!prop)
167-
return -EINVAL;
271+
return 0;
168272

169273
data->max_brightness = length / sizeof(u32);
170274

@@ -294,6 +398,14 @@ static int pwm_backlight_parse_dt(struct device *dev,
294398
{
295399
return -ENODEV;
296400
}
401+
402+
static
403+
int pwm_backlight_brightness_default(struct device *dev,
404+
struct platform_pwm_backlight_data *data,
405+
unsigned int period)
406+
{
407+
return -ENODEV;
408+
}
297409
#endif
298410

299411
static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
@@ -334,7 +446,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
334446
struct backlight_device *bl;
335447
struct device_node *node = pdev->dev.of_node;
336448
struct pwm_bl_data *pb;
449+
struct pwm_state state;
337450
struct pwm_args pargs;
451+
unsigned int i;
338452
int ret;
339453

340454
if (!data) {
@@ -359,17 +473,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
359473
goto err_alloc;
360474
}
361475

362-
if (data->levels) {
363-
unsigned int i;
364-
365-
for (i = 0; i <= data->max_brightness; i++)
366-
if (data->levels[i] > pb->scale)
367-
pb->scale = data->levels[i];
368-
369-
pb->levels = data->levels;
370-
} else
371-
pb->scale = data->max_brightness;
372-
373476
pb->notify = data->notify;
374477
pb->notify_after = data->notify_after;
375478
pb->check_fb = data->check_fb;
@@ -436,6 +539,26 @@ static int pwm_backlight_probe(struct platform_device *pdev)
436539

437540
dev_dbg(&pdev->dev, "got pwm for backlight\n");
438541

542+
if (!data->levels) {
543+
/* Get the PWM period (in nanoseconds) */
544+
pwm_get_state(pb->pwm, &state);
545+
546+
ret = pwm_backlight_brightness_default(&pdev->dev, data,
547+
state.period);
548+
if (ret < 0) {
549+
dev_err(&pdev->dev,
550+
"failed to setup default brightness table\n");
551+
goto err_alloc;
552+
}
553+
}
554+
555+
for (i = 0; i <= data->max_brightness; i++) {
556+
if (data->levels[i] > pb->scale)
557+
pb->scale = data->levels[i];
558+
559+
pb->levels = data->levels;
560+
}
561+
439562
/*
440563
* FIXME: pwm_apply_args() should be removed when switching to
441564
* the atomic PWM API.

0 commit comments

Comments
 (0)