pwm_get_state(pb->pwm, &state);
state.duty_cycle = 0; /* * We cannot assume a disabled PWM to drive its output to the * inactive state. If we have an enable GPIO and/or a regulator * we assume that this isn't relevant and we can disable the PWM * to save power. If however there is neither an enable GPIO nor * a regulator keep the PWM on be sure to get a constant * inactive output.
*/
state.enabled = !pb->power_supply && !pb->enable_gpio;
pwm_apply_might_sleep(pb->pwm, &state);
}
if (pb->notify_after)
pb->notify_after(pb->dev, brightness);
/* * CIE lightness to PWM conversion. * * The CIE 1931 lightness formula is what actually describes how we perceive * light: * Y = (L* / 903.3) if L* ≤ 8 * Y = ((L* + 16) / 116)^3 if L* > 8 * * 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(unsignedint lightness)
{
u64 retval;
/* * @lightness is given as a number between 0 and 1, expressed * as a fixed-point number in scale * PWM_LUMINANCE_SCALE. Convert to a percentage, still * expressed as a fixed-point number, so the above formulas * can be applied.
*/
lightness *= 100; if (lightness <= (8 * PWM_LUMINANCE_SCALE)) {
retval = DIV_ROUND_CLOSEST(lightness * 10, 9033);
} else {
retval = (lightness + (16 * PWM_LUMINANCE_SCALE)) / 116;
retval *= retval * retval;
retval += 1ULL << (2*PWM_LUMINANCE_SHIFT - 1);
retval >>= 2*PWM_LUMINANCE_SHIFT;
}
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, unsignedint period)
{ unsignedint i;
u64 retval;
/* * Once we have 4096 levels there's little point going much higher... * neither interactive sliders nor animation benefits from having * more values in the table.
*/
data->max_brightness =
min((int)DIV_ROUND_UP(period, fls(period)), 4096);
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) * period;
retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE); if (retval > UINT_MAX) return -EINVAL;
data->levels[i] = (unsignedint)retval;
}
/* * These values are optional and set as 0 by default, the out values * are modified only if a valid u32 value can be decoded.
*/
of_property_read_u32(node, "post-pwm-on-delay-ms",
&data->post_pwm_on_delay);
of_property_read_u32(node, "pwm-off-delay-ms", &data->pwm_off_delay);
/* * 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 0;
num_levels = length / sizeof(u32);
/* read brightness levels from DT property */ if (num_levels > 0) {
data->levels = devm_kcalloc(dev, num_levels, sizeof(*data->levels), GFP_KERNEL); if (!data->levels) return -ENOMEM;
ret = of_property_read_u32_array(node, "brightness-levels",
data->levels,
num_levels); if (ret < 0) return ret;
ret = of_property_read_u32(node, "default-brightness-level",
&value); if (ret < 0) 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) { unsignedint num_input_levels = num_levels; unsignedint i;
u32 x1, x2, x, dx;
u32 y1, y2;
s64 dy;
/* * Recalculate the number of brightness levels, now * taking in consideration the number of interpolated * steps between two levels.
*/
num_levels = (num_input_levels - 1) * num_steps + 1;
dev_dbg(dev, "new number of brightness levels: %d\n",
num_levels);
/* * Create a new table of brightness levels with all the * interpolated steps.
*/
table = devm_kcalloc(dev, num_levels, sizeof(*table),
GFP_KERNEL); if (!table) return -ENOMEM; /* * Fill the interpolated table[x] = y * by draw lines between each (x1, y1) to (x2, y2).
*/
dx = num_steps; for (i = 0; i < num_input_levels - 1; i++) {
x1 = i * dx;
x2 = x1 + dx;
y1 = data->levels[i];
y2 = data->levels[i + 1];
dy = (s64)y2 - y1;
for (x = x1; x < x2; x++) {
table[x] = y1 +
div_s64(dy * (x - x1), dx);
}
} /* Fill in the last point, since no line starts here. */
table[x2] = y2;
/* * 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;
}
/* * If the enable GPIO is present, observable (either as input * or output) and off then the backlight is not currently active.
* */ if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0)
active = false;
if (pb->power_supply && !regulator_is_enabled(pb->power_supply))
active = false;
if (!pwm_is_enabled(pb->pwm))
active = false;
/* * Synchronize the enable_gpio with the observed state of the * hardware.
*/
gpiod_direction_output(pb->enable_gpio, active);
/* * Do not change pb->enabled here! pb->enabled essentially * tells us if we own one of the regulator's use counts and * right now we do not.
*/
/* Not booted with device tree or no phandle link to the node */ if (!node || !node->phandle) return BACKLIGHT_POWER_ON;
/* * If the driver is probed from the device tree and there is a * phandle link pointing to the backlight node, it is safe to * assume that another driver will enable the backlight at the * appropriate time. Therefore, if it is disabled, keep it so.
*/ return active ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
}
pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
GPIOD_ASIS); if (IS_ERR(pb->enable_gpio)) {
ret = dev_err_probe(&pdev->dev, PTR_ERR(pb->enable_gpio), "failed to acquire enable GPIO\n"); goto err_alloc;
}
pb->power_supply = devm_regulator_get_optional(&pdev->dev, "power"); if (IS_ERR(pb->power_supply)) {
ret = PTR_ERR(pb->power_supply); if (ret == -ENODEV) {
pb->power_supply = NULL;
} else {
dev_err_probe(&pdev->dev, ret, "failed to acquire power regulator\n"); goto err_alloc;
}
}
pb->pwm = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(pb->pwm)) {
ret = dev_err_probe(&pdev->dev, PTR_ERR(pb->pwm), "unable to request PWM\n"); goto err_alloc;
}
dev_dbg(&pdev->dev, "got pwm for backlight\n");
/* Sync up PWM state. */
pwm_init_state(pb->pwm, &state);
/* * The DT case will set the pwm_period_ns field to 0 and store the * period, parsed from the DT, in the PWM device. For the non-DT case, * set the period from platform data if it has not already been set * via the PWM lookup table.
*/ if (!state.period && (data->pwm_period_ns > 0))
state.period = data->pwm_period_ns;
ret = pwm_apply_might_sleep(pb->pwm, &state); if (ret) {
dev_err_probe(&pdev->dev, ret, "failed to apply initial PWM state"); goto err_alloc;
}
/* * For the DT case, only when brightness levels is defined * data->levels is filled. For the non-DT case, data->levels * can come from platform data, however is not usual.
*/ for (i = 0; i <= data->max_brightness; i++) if (data->levels[i] > pb->scale)
pb->scale = data->levels[i];
if (pwm_backlight_is_linear(data))
props.scale = BACKLIGHT_SCALE_LINEAR; else
props.scale = BACKLIGHT_SCALE_NON_LINEAR;
} elseif (!data->max_brightness) { /* * If no brightness levels are provided and max_brightness is * not set, use the default brightness table. For the DT case, * max_brightness is set to 0 when brightness levels is not * specified. For the non-DT case, max_brightness is usually * set to some value.
*/
/* 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_probe(&pdev->dev, ret, "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;
}
props.scale = BACKLIGHT_SCALE_NON_LINEAR;
} else { /* * That only happens for the non-DT case, where platform data * sets the max_brightness value.
*/
pb->scale = data->max_brightness;
}
/* * Note that disabling the PWM doesn't guarantee that the output stays * at its inactive state. However without the PWM disabled, the PWM * driver refuses to suspend. So disable here even though this might * enable the backlight on poorly designed boards.
*/
pwm_get_state(pb->pwm, &state);
state.duty_cycle = 0;
state.enabled = false;
pwm_apply_might_sleep(pb->pwm, &state);
if (pb->notify_after)
pb->notify_after(pb->dev, 0);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.