@@ -406,9 +406,90 @@ static bool pwm_backlight_is_linear(struct platform_pwm_backlight_data *data)
return true;
}
+/*
+ * Inherit the initial power state from the hardware.
+ *
+ * This function provides the features necessary to achieve a flicker-free boot
+ * sequence regardless of the initial state of the backlight.
+ *
+ * There are two factors that affect the behaviour of this function.
+ *
+ * 1. Whether the backlight was on or off when the kernel was booted. We
+ * currently determine the state of the backlight by checking if the PWM is
+ * enabled, whether the regulator (if there is one) is enabled and whether
+ * the enable_gpio (if there is one) is asserted. All must be enabled for
+ * the backlight to be on.
+ *
+ * 2. Whether the backlight is linked to a display device. This matters because
+ * when there is a linked display is will automatically handle the
+ * backlight as part of its own blank/unblanking.
+ *
+ * This gives us four possible cases.
+ *
+ * Backlight initially off, display linked:
+ *
+ * The backlight must remain off (a.k.a. FB_BLANK_POWERDOWN) during and after
+ * the backlight probe. This allows a splash screen to be drawn before the
+ * backlight is enabled by the display driver. This avoids a flicker when the
+ * backlight comes on (which typically changes the black level slightly)
+ * before the splash image has been drawn.
+ *
+ * Backlight initially on, display linked:
+ *
+ * The backlight must remain on (a.k.a. FB_BLANK_UNBLANK) during and after
+ * the backlight probe. This allows a bootloader to show a splash screen and
+ * for the display system (including the backlight) to continue showing the
+ * splash image until the kernel is ready to take over the display and draw
+ * something else.
+ *
+ * Backlight initially off, no display:
+ *
+ * The backlight must transition from off to on (a.k.a. FB_BLANK_UNBLANK)
+ * during the backlight probe. This is largely a legacy case. We must
+ * unblank the backlight at boot because some userspaces are not
+ * capable of changing the power state of a free-standing backlight
+ * (they only know how to set the brightness level).
+ *
+ * Backlight initially on, no display:
+ *
+ * Identical to the initially on, display linked case.
+ *
+ * Note: In both cases where backlight is initially off then we must
+ * explicitly deassert the enable_gpio in order to ensure we
+ * honour the post_pwm_on_delay when the backlight is eventually
+ * activated. This is required regardless of both the initial state of
+ * the enable pin and whether we intend to activate the backlight during
+ * the probe.
+ */
static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
{
- struct device_node *node = pb->dev->of_node;
+ struct device_node *node = pb->dev->of_node; bool active = true;
+
+ /*
+ * 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 (!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.
+ */
+ if (pb->enable_gpio)
+ 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)
@@ -420,20 +501,7 @@ static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
* assume that another driver will enable the backlight at the
* appropriate time. Therefore, if it is disabled, keep it so.
*/
-
- /* if the enable GPIO is disabled, do not enable the backlight */
- if (pb->enable_gpio && gpiod_get_value_cansleep(pb->enable_gpio) == 0)
- return FB_BLANK_POWERDOWN;
-
- /* The regulator is disabled, do not enable the backlight */
- if (!regulator_is_enabled(pb->power_supply))
- return FB_BLANK_POWERDOWN;
-
- /* The PWM is disabled, keep it like this */
- if (!pwm_is_enabled(pb->pwm))
- return FB_BLANK_POWERDOWN;
-
- return FB_BLANK_UNBLANK;
+ return active ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
}
static int pwm_backlight_probe(struct platform_device *pdev)
@@ -486,18 +554,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
goto err_alloc;
}
- /*
- * If the GPIO is not known to be already configured as output, that
- * is, if gpiod_get_direction returns either 1 or -EINVAL, change the
- * direction to output and set the GPIO as active.
- * Do not force the GPIO to active when it was already output as it
- * could cause backlight flickering or we would enable the backlight too
- * early. Leave the decision of the initial backlight state for later.
- */
- if (pb->enable_gpio &&
- gpiod_get_direction(pb->enable_gpio) != 0)
- gpiod_direction_output(pb->enable_gpio, 1);
-
pb->power_supply = devm_regulator_get(&pdev->dev, "power");
if (IS_ERR(pb->power_supply)) {
ret = PTR_ERR(pb->power_supply);