- 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:
|
||||
- compatible: "pwm-backlight"
|
||||
- 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
|
||||
|
||||
Optional properties:
|
||||
|
@ -21,6 +14,19 @@ Optional properties:
|
|||
and enabling the backlight using GPIO.
|
||||
- pwm-off-delay-ms: Delay in ms between disabling the backlight using GPIO
|
||||
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
|
||||
[1]: Documentation/devicetree/bindings/gpio/gpio.txt
|
||||
|
@ -39,3 +45,17 @@ Example:
|
|||
post-pwm-on-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)) {
|
||||
case ADP8863_MANUFID:
|
||||
data->gdwn_dis = !!pdata->gdwn_dis;
|
||||
/* fall through */
|
||||
case ADP8860_MANUFID:
|
||||
data->en_ambl_sens = !!pdata->en_ambl_sens;
|
||||
break;
|
||||
|
|
|
@ -143,11 +143,116 @@ static const struct backlight_ops pwm_backlight_ops = {
|
|||
};
|
||||
|
||||
#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,
|
||||
struct platform_pwm_backlight_data *data)
|
||||
{
|
||||
struct device_node *node = dev->of_node;
|
||||
unsigned int num_levels = 0;
|
||||
unsigned int levels_count;
|
||||
unsigned int num_steps = 0;
|
||||
struct property *prop;
|
||||
unsigned int *table;
|
||||
int length;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
@ -157,16 +262,20 @@ static int pwm_backlight_parse_dt(struct device *dev,
|
|||
|
||||
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);
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
|
||||
data->max_brightness = length / sizeof(u32);
|
||||
|
||||
/* read brightness levels from DT property */
|
||||
if (data->max_brightness > 0) {
|
||||
size_t size = sizeof(*data->levels) * data->max_brightness;
|
||||
unsigned int i, j, n = 0;
|
||||
|
||||
data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
if (!data->levels)
|
||||
|
@ -184,6 +293,84 @@ static int pwm_backlight_parse_dt(struct device *dev,
|
|||
return ret;
|
||||
|
||||
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--;
|
||||
}
|
||||
|
||||
|
@ -211,6 +398,14 @@ static int pwm_backlight_parse_dt(struct device *dev,
|
|||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static
|
||||
int pwm_backlight_brightness_default(struct device *dev,
|
||||
struct platform_pwm_backlight_data *data,
|
||||
unsigned int period)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
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 device_node *node = pdev->dev.of_node;
|
||||
struct pwm_bl_data *pb;
|
||||
struct pwm_state state;
|
||||
struct pwm_args pargs;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!data) {
|
||||
|
@ -276,17 +473,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
|
|||
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_after = data->notify_after;
|
||||
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");
|
||||
|
||||
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
|
||||
* the atomic PWM API.
|
||||
|
|
|
@ -79,7 +79,6 @@ struct backlight_properties {
|
|||
/* Backlight type */
|
||||
enum backlight_type type;
|
||||
/* Flags used to signal drivers of state changes */
|
||||
/* Upper 4 bits are reserved for driver internal use */
|
||||
unsigned int state;
|
||||
|
||||
#define BL_CORE_SUSPENDED (1 << 0) /* backlight is suspended */
|
||||
|
|
Loading…
Reference in a new issue