mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-06 08:46:46 +00:00
- Core Frameworks
- Remove unused/obsolete code/comments - New Functionality - Allow less granular brightness specification for high-res PWMs; pwm_bl - Align brightness {inc,dec}rements with that perceived by the human-eye; pwm_bl - Fix-ups - Prepare for the introduction of -Wimplicit-fall-through; adp8860_bl - Bug Fixes - Fix uninitialised variable; pwm_bl -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAlt6gwcACgkQUa+KL4f8 d2EKfA//Vbomk1W0DtqBuyKUask0gcaunuICWtKZG5TnUOJctp7Jmno4UrCmkvHD FDoOJcq3LoI/xfh68tEhvUHqYbFQrOPJC0K8jCszVzdACUsz1+Gw1oCgmQUPGg9r TG9QlQZuimbqWzmH2xQquH2H6yVe+2xJZ1U6X4KPLP0OKkSwrOfm6sYrV7Qus+uw 1T77j8s9c5n+qiyAQt7vUBbwBDAhn33QMNqLU72QR6KmJ4A8VS8nyPFTZGXeX8nz WvrpJ3wqKRHXW2Fe4YKzs4fXMIDJpjQn8Ls8vn8ESRL+Pd+UvGsLUyZ5uYG9Opht NN7UGlSRjRdaX3NiiFH8/XS16bnQk2lsrVjOIfvaSmZbMCND2UAc7M1xkxhBG6h/ LKwi9RJiTxYswf/XgSAgMfv7Ge1CCIAZljMnDuJZmMEW49tKkAbc12tNypw/yujX yg0ynHb1yBGUM4EbEhuhfgLx067ARXv/fuvMdfxcUlqG1RBhrpA/xw4OueILemwM dJE9IwCRChWxgnlq2Oirnt4pPjR1k5tui4pR5qXvULm8J2PLLSrBM3WUlbcE/m0f jxkSuEAWLvyd+dPwl1dNm9uLSho8N8TR2f921AUeyFmFbozPeSEGUiAeMCY0ly5M IUg5caqVF3NvkKlW+lmL9Ik8Gd6j1JGEnaJE8NUKRzdu4ZLev6k= =C/pI -----END PGP SIGNATURE----- Merge tag 'backlight-next-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight Pull backlight updates from Lee Jones: "Core Framework: - Remove unused/obsolete code/comments New Functionality: - Allow less granular brightness specification for high-res PWMs; pwm_bl - Align brightness {inc,dec}rements with that perceived by the human-eye; pwm_bl Fix-ups: - Prepare for the introduction of -Wimplicit-fall-through; adp8860_bl Bug Fixes: - Fix uninitialised variable; pwm_bl" * tag 'backlight-next-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/backlight: backlight: pwm_bl: Fix uninitialized variable backlight: adp8860: Mark expected switch fall-through backlight: Remove obsolete comment for ->state dt-bindings: pwm-backlight: Move brightness-levels to optional backlight: pwm_bl: Compute brightness of LED linearly to human eye dt-bindings: pwm-backlight: Add a num-interpolation-steps property backlight: pwm_bl: Linear interpolation between brightness-levels
This commit is contained in:
commit
61c4fc1eaf
4 changed files with 247 additions and 21 deletions
|
@ -3,13 +3,6 @@ pwm-backlight bindings
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "pwm-backlight"
|
- compatible: "pwm-backlight"
|
||||||
- pwms: OF device-tree PWM specification (see PWM binding[0])
|
- pwms: OF device-tree PWM specification (see PWM binding[0])
|
||||||
- brightness-levels: Array of distinct brightness levels. Typically these
|
|
||||||
are in the range from 0 to 255, but any range starting at 0 will do.
|
|
||||||
The actual brightness level (PWM duty cycle) will be interpolated
|
|
||||||
from these values. 0 means a 0% duty cycle (darkest/off), while the
|
|
||||||
last value in the array represents a 100% duty cycle (brightest).
|
|
||||||
- default-brightness-level: the default brightness level (index into the
|
|
||||||
array defined by the "brightness-levels" property)
|
|
||||||
- power-supply: regulator for supply voltage
|
- power-supply: regulator for supply voltage
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
@ -21,6 +14,19 @@ Optional properties:
|
||||||
and enabling the backlight using GPIO.
|
and enabling the backlight using GPIO.
|
||||||
- pwm-off-delay-ms: Delay in ms between disabling the backlight using GPIO
|
- pwm-off-delay-ms: Delay in ms between disabling the backlight using GPIO
|
||||||
and setting PWM value to 0.
|
and setting PWM value to 0.
|
||||||
|
- brightness-levels: Array of distinct brightness levels. Typically these
|
||||||
|
are in the range from 0 to 255, but any range starting at
|
||||||
|
0 will do. The actual brightness level (PWM duty cycle)
|
||||||
|
will be interpolated from these values. 0 means a 0% duty
|
||||||
|
cycle (darkest/off), while the last value in the array
|
||||||
|
represents a 100% duty cycle (brightest).
|
||||||
|
- default-brightness-level: The default brightness level (index into the
|
||||||
|
array defined by the "brightness-levels" property).
|
||||||
|
- num-interpolated-steps: Number of interpolated steps between each value
|
||||||
|
of brightness-levels table. This way a high
|
||||||
|
resolution pwm duty cycle can be used without
|
||||||
|
having to list out every possible value in the
|
||||||
|
brightness-level array.
|
||||||
|
|
||||||
[0]: Documentation/devicetree/bindings/pwm/pwm.txt
|
[0]: Documentation/devicetree/bindings/pwm/pwm.txt
|
||||||
[1]: Documentation/devicetree/bindings/gpio/gpio.txt
|
[1]: Documentation/devicetree/bindings/gpio/gpio.txt
|
||||||
|
@ -39,3 +45,17 @@ Example:
|
||||||
post-pwm-on-delay-ms = <10>;
|
post-pwm-on-delay-ms = <10>;
|
||||||
pwm-off-delay-ms = <10>;
|
pwm-off-delay-ms = <10>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Example using num-interpolation-steps:
|
||||||
|
|
||||||
|
backlight {
|
||||||
|
compatible = "pwm-backlight";
|
||||||
|
pwms = <&pwm 0 5000000>;
|
||||||
|
|
||||||
|
brightness-levels = <0 2048 4096 8192 16384 65535>;
|
||||||
|
num-interpolated-steps = <2048>;
|
||||||
|
default-brightness-level = <4096>;
|
||||||
|
|
||||||
|
power-supply = <&vdd_bl_reg>;
|
||||||
|
enable-gpios = <&gpio 58 0>;
|
||||||
|
};
|
||||||
|
|
|
@ -690,6 +690,7 @@ static int adp8860_probe(struct i2c_client *client,
|
||||||
switch (ADP8860_MANID(reg_val)) {
|
switch (ADP8860_MANID(reg_val)) {
|
||||||
case ADP8863_MANUFID:
|
case ADP8863_MANUFID:
|
||||||
data->gdwn_dis = !!pdata->gdwn_dis;
|
data->gdwn_dis = !!pdata->gdwn_dis;
|
||||||
|
/* fall through */
|
||||||
case ADP8860_MANUFID:
|
case ADP8860_MANUFID:
|
||||||
data->en_ambl_sens = !!pdata->en_ambl_sens;
|
data->en_ambl_sens = !!pdata->en_ambl_sens;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -143,11 +143,116 @@ static const struct backlight_ops pwm_backlight_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
|
#define PWM_LUMINANCE_SCALE 10000 /* luminance scale */
|
||||||
|
|
||||||
|
/* An integer based power function */
|
||||||
|
static u64 int_pow(u64 base, int exp)
|
||||||
|
{
|
||||||
|
u64 result = 1;
|
||||||
|
|
||||||
|
while (exp) {
|
||||||
|
if (exp & 1)
|
||||||
|
result *= base;
|
||||||
|
exp >>= 1;
|
||||||
|
base *= base;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CIE lightness to PWM conversion.
|
||||||
|
*
|
||||||
|
* The CIE 1931 lightness formula is what actually describes how we perceive
|
||||||
|
* light:
|
||||||
|
* Y = (L* / 902.3) if L* ≤ 0.08856
|
||||||
|
* Y = ((L* + 16) / 116)^3 if L* > 0.08856
|
||||||
|
*
|
||||||
|
* Where Y is the luminance, the amount of light coming out of the screen, and
|
||||||
|
* is a number between 0.0 and 1.0; and L* is the lightness, how bright a human
|
||||||
|
* perceives the screen to be, and is a number between 0 and 100.
|
||||||
|
*
|
||||||
|
* The following function does the fixed point maths needed to implement the
|
||||||
|
* above formula.
|
||||||
|
*/
|
||||||
|
static u64 cie1931(unsigned int lightness, unsigned int scale)
|
||||||
|
{
|
||||||
|
u64 retval;
|
||||||
|
|
||||||
|
lightness *= 100;
|
||||||
|
if (lightness <= (8 * scale)) {
|
||||||
|
retval = DIV_ROUND_CLOSEST_ULL(lightness * 10, 9023);
|
||||||
|
} else {
|
||||||
|
retval = int_pow((lightness + (16 * scale)) / 116, 3);
|
||||||
|
retval = DIV_ROUND_CLOSEST_ULL(retval, (scale * scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a default correction table for PWM values to create linear brightness
|
||||||
|
* for LED based backlights using the CIE1931 algorithm.
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
int pwm_backlight_brightness_default(struct device *dev,
|
||||||
|
struct platform_pwm_backlight_data *data,
|
||||||
|
unsigned int period)
|
||||||
|
{
|
||||||
|
unsigned int counter = 0;
|
||||||
|
unsigned int i, n;
|
||||||
|
u64 retval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Count the number of bits needed to represent the period number. The
|
||||||
|
* number of bits is used to calculate the number of levels used for the
|
||||||
|
* brightness-levels table, the purpose of this calculation is have a
|
||||||
|
* pre-computed table with enough levels to get linear brightness
|
||||||
|
* perception. The period is divided by the number of bits so for a
|
||||||
|
* 8-bit PWM we have 255 / 8 = 32 brightness levels or for a 16-bit PWM
|
||||||
|
* we have 65535 / 16 = 4096 brightness levels.
|
||||||
|
*
|
||||||
|
* Note that this method is based on empirical testing on different
|
||||||
|
* devices with PWM of 8 and 16 bits of resolution.
|
||||||
|
*/
|
||||||
|
n = period;
|
||||||
|
while (n) {
|
||||||
|
counter += n % 2;
|
||||||
|
n >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->max_brightness = DIV_ROUND_UP(period, counter);
|
||||||
|
data->levels = devm_kcalloc(dev, data->max_brightness,
|
||||||
|
sizeof(*data->levels), GFP_KERNEL);
|
||||||
|
if (!data->levels)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Fill the table using the cie1931 algorithm */
|
||||||
|
for (i = 0; i < data->max_brightness; i++) {
|
||||||
|
retval = cie1931((i * PWM_LUMINANCE_SCALE) /
|
||||||
|
data->max_brightness, PWM_LUMINANCE_SCALE) *
|
||||||
|
period;
|
||||||
|
retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE);
|
||||||
|
if (retval > UINT_MAX)
|
||||||
|
return -EINVAL;
|
||||||
|
data->levels[i] = (unsigned int)retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dft_brightness = data->max_brightness / 2;
|
||||||
|
data->max_brightness--;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int pwm_backlight_parse_dt(struct device *dev,
|
static int pwm_backlight_parse_dt(struct device *dev,
|
||||||
struct platform_pwm_backlight_data *data)
|
struct platform_pwm_backlight_data *data)
|
||||||
{
|
{
|
||||||
struct device_node *node = dev->of_node;
|
struct device_node *node = dev->of_node;
|
||||||
|
unsigned int num_levels = 0;
|
||||||
|
unsigned int levels_count;
|
||||||
|
unsigned int num_steps = 0;
|
||||||
struct property *prop;
|
struct property *prop;
|
||||||
|
unsigned int *table;
|
||||||
int length;
|
int length;
|
||||||
u32 value;
|
u32 value;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -157,16 +262,20 @@ static int pwm_backlight_parse_dt(struct device *dev,
|
||||||
|
|
||||||
memset(data, 0, sizeof(*data));
|
memset(data, 0, sizeof(*data));
|
||||||
|
|
||||||
/* determine the number of brightness levels */
|
/*
|
||||||
|
* Determine the number of brightness levels, if this property is not
|
||||||
|
* set a default table of brightness levels will be used.
|
||||||
|
*/
|
||||||
prop = of_find_property(node, "brightness-levels", &length);
|
prop = of_find_property(node, "brightness-levels", &length);
|
||||||
if (!prop)
|
if (!prop)
|
||||||
return -EINVAL;
|
return 0;
|
||||||
|
|
||||||
data->max_brightness = length / sizeof(u32);
|
data->max_brightness = length / sizeof(u32);
|
||||||
|
|
||||||
/* read brightness levels from DT property */
|
/* read brightness levels from DT property */
|
||||||
if (data->max_brightness > 0) {
|
if (data->max_brightness > 0) {
|
||||||
size_t size = sizeof(*data->levels) * data->max_brightness;
|
size_t size = sizeof(*data->levels) * data->max_brightness;
|
||||||
|
unsigned int i, j, n = 0;
|
||||||
|
|
||||||
data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
|
data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||||
if (!data->levels)
|
if (!data->levels)
|
||||||
|
@ -184,6 +293,84 @@ static int pwm_backlight_parse_dt(struct device *dev,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
data->dft_brightness = value;
|
data->dft_brightness = value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This property is optional, if is set enables linear
|
||||||
|
* interpolation between each of the values of brightness levels
|
||||||
|
* and creates a new pre-computed table.
|
||||||
|
*/
|
||||||
|
of_property_read_u32(node, "num-interpolated-steps",
|
||||||
|
&num_steps);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure that there is at least two entries in the
|
||||||
|
* brightness-levels table, otherwise we can't interpolate
|
||||||
|
* between two points.
|
||||||
|
*/
|
||||||
|
if (num_steps) {
|
||||||
|
if (data->max_brightness < 2) {
|
||||||
|
dev_err(dev, "can't interpolate\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recalculate the number of brightness levels, now
|
||||||
|
* taking in consideration the number of interpolated
|
||||||
|
* steps between two levels.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < data->max_brightness - 1; i++) {
|
||||||
|
if ((data->levels[i + 1] - data->levels[i]) /
|
||||||
|
num_steps)
|
||||||
|
num_levels += num_steps;
|
||||||
|
else
|
||||||
|
num_levels++;
|
||||||
|
}
|
||||||
|
num_levels++;
|
||||||
|
dev_dbg(dev, "new number of brightness levels: %d\n",
|
||||||
|
num_levels);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new table of brightness levels with all the
|
||||||
|
* interpolated steps.
|
||||||
|
*/
|
||||||
|
size = sizeof(*table) * num_levels;
|
||||||
|
table = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||||
|
if (!table)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Fill the interpolated table. */
|
||||||
|
levels_count = 0;
|
||||||
|
for (i = 0; i < data->max_brightness - 1; i++) {
|
||||||
|
value = data->levels[i];
|
||||||
|
n = (data->levels[i + 1] - value) / num_steps;
|
||||||
|
if (n > 0) {
|
||||||
|
for (j = 0; j < num_steps; j++) {
|
||||||
|
table[levels_count] = value;
|
||||||
|
value += n;
|
||||||
|
levels_count++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
table[levels_count] = data->levels[i];
|
||||||
|
levels_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table[levels_count] = data->levels[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As we use interpolation lets remove current
|
||||||
|
* brightness levels table and replace for the
|
||||||
|
* new interpolated table.
|
||||||
|
*/
|
||||||
|
devm_kfree(dev, data->levels);
|
||||||
|
data->levels = table;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reassign max_brightness value to the new total number
|
||||||
|
* of brightness levels.
|
||||||
|
*/
|
||||||
|
data->max_brightness = num_levels;
|
||||||
|
}
|
||||||
|
|
||||||
data->max_brightness--;
|
data->max_brightness--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +398,14 @@ static int pwm_backlight_parse_dt(struct device *dev,
|
||||||
{
|
{
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int pwm_backlight_brightness_default(struct device *dev,
|
||||||
|
struct platform_pwm_backlight_data *data,
|
||||||
|
unsigned int period)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
|
static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
|
||||||
|
@ -251,7 +446,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
|
||||||
struct backlight_device *bl;
|
struct backlight_device *bl;
|
||||||
struct device_node *node = pdev->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct pwm_bl_data *pb;
|
struct pwm_bl_data *pb;
|
||||||
|
struct pwm_state state;
|
||||||
struct pwm_args pargs;
|
struct pwm_args pargs;
|
||||||
|
unsigned int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
@ -276,17 +473,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
|
||||||
goto err_alloc;
|
goto err_alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->levels) {
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i <= data->max_brightness; i++)
|
|
||||||
if (data->levels[i] > pb->scale)
|
|
||||||
pb->scale = data->levels[i];
|
|
||||||
|
|
||||||
pb->levels = data->levels;
|
|
||||||
} else
|
|
||||||
pb->scale = data->max_brightness;
|
|
||||||
|
|
||||||
pb->notify = data->notify;
|
pb->notify = data->notify;
|
||||||
pb->notify_after = data->notify_after;
|
pb->notify_after = data->notify_after;
|
||||||
pb->check_fb = data->check_fb;
|
pb->check_fb = data->check_fb;
|
||||||
|
@ -353,6 +539,26 @@ static int pwm_backlight_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "got pwm for backlight\n");
|
dev_dbg(&pdev->dev, "got pwm for backlight\n");
|
||||||
|
|
||||||
|
if (!data->levels) {
|
||||||
|
/* Get the PWM period (in nanoseconds) */
|
||||||
|
pwm_get_state(pb->pwm, &state);
|
||||||
|
|
||||||
|
ret = pwm_backlight_brightness_default(&pdev->dev, data,
|
||||||
|
state.period);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"failed to setup default brightness table\n");
|
||||||
|
goto err_alloc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i <= data->max_brightness; i++) {
|
||||||
|
if (data->levels[i] > pb->scale)
|
||||||
|
pb->scale = data->levels[i];
|
||||||
|
|
||||||
|
pb->levels = data->levels;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FIXME: pwm_apply_args() should be removed when switching to
|
* FIXME: pwm_apply_args() should be removed when switching to
|
||||||
* the atomic PWM API.
|
* the atomic PWM API.
|
||||||
|
|
|
@ -79,7 +79,6 @@ struct backlight_properties {
|
||||||
/* Backlight type */
|
/* Backlight type */
|
||||||
enum backlight_type type;
|
enum backlight_type type;
|
||||||
/* Flags used to signal drivers of state changes */
|
/* Flags used to signal drivers of state changes */
|
||||||
/* Upper 4 bits are reserved for driver internal use */
|
|
||||||
unsigned int state;
|
unsigned int state;
|
||||||
|
|
||||||
#define BL_CORE_SUSPENDED (1 << 0) /* backlight is suspended */
|
#define BL_CORE_SUSPENDED (1 << 0) /* backlight is suspended */
|
||||||
|
|
Loading…
Reference in a new issue