diff mbox series

[RFC] drm/panel: Make backlight attachment lazy

Message ID 20201208044446.973238-1-bjorn.andersson@linaro.org (mailing list archive)
State New, archived
Headers show
Series [RFC] drm/panel: Make backlight attachment lazy | expand

Commit Message

Bjorn Andersson Dec. 8, 2020, 4:44 a.m. UTC
Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
means of generating a PWM signal for backlight control of the attached
panel. The provided PWM chip is typically controlled by the
pwm-backlight driver, which if tied to the panel will provide DPMS.

But with the current implementation the panel will refuse to probe
because the bridge driver has yet to probe and register the PWM chip,
and the bridge driver will refuse to probe because it's unable to find
the panel.

Mitigate this catch-22 situation by allowing the panel driver to probe
and retry the attachment of the backlight as the panel is turned on or
off.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
 drivers/gpu/drm/drm_panel.c | 47 +++++++++++++++++++++++++++----------
 include/drm/drm_panel.h     |  8 +++++++
 2 files changed, 43 insertions(+), 12 deletions(-)

Comments

Thierry Reding Dec. 8, 2020, 12:47 p.m. UTC | #1
On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> means of generating a PWM signal for backlight control of the attached
> panel. The provided PWM chip is typically controlled by the
> pwm-backlight driver, which if tied to the panel will provide DPMS.
> 
> But with the current implementation the panel will refuse to probe
> because the bridge driver has yet to probe and register the PWM chip,
> and the bridge driver will refuse to probe because it's unable to find
> the panel.

What you're describing is basically a circular dependency. Can't we get
rid of that in some other way? Why exactly does the bridge driver refuse
to probe if the panel can't be found?

In other words, I see how the bridge would /use/ the panel in that it
forward a video stream to it. But how does the panel /use/ the bridge?

Thierry
Bjorn Andersson Dec. 8, 2020, 9:39 p.m. UTC | #2
On Mon 07 Dec 23:48 CST 2020, Sam Ravnborg wrote:

> Hi Bjorn,
> On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> > Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> > means of generating a PWM signal for backlight control of the attached
> > panel. The provided PWM chip is typically controlled by the
> > pwm-backlight driver, which if tied to the panel will provide DPMS.
> > 
> > But with the current implementation the panel will refuse to probe
> > because the bridge driver has yet to probe and register the PWM chip,
> > and the bridge driver will refuse to probe because it's unable to find
> > the panel.
> > 
> > Mitigate this catch-22 situation by allowing the panel driver to probe
> > and retry the attachment of the backlight as the panel is turned on or
> > off.
> > 
> > Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> > ---
> >  drivers/gpu/drm/drm_panel.c | 47 +++++++++++++++++++++++++++----------
> >  include/drm/drm_panel.h     |  8 +++++++
> >  2 files changed, 43 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
> > index f634371c717a..7487329bd22d 100644
> > --- a/drivers/gpu/drm/drm_panel.c
> > +++ b/drivers/gpu/drm/drm_panel.c
> > @@ -43,6 +43,34 @@ static LIST_HEAD(panel_list);
> >   * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add().
> >   */
> >  
> > +#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
> > +static int drm_panel_of_backlight_lazy(struct drm_panel *panel)
> > +{
> > +	struct backlight_device *backlight;
> > +
> > +	if (!panel || !panel->dev)
> > +		return -EINVAL;
> > +
> > +	backlight = devm_of_find_backlight(panel->dev);
> > +
> > +	if (IS_ERR(backlight)) {
> > +		if (PTR_ERR(backlight) == -EPROBE_DEFER) {
> > +			panel->backlight_init_pending = true;
> > +			return 0;
> > +		}
> > +
> > +		return PTR_ERR(backlight);
> Use dev_err_probe()
> 

I need special handling of EPROBE_DEFER, both in terms of remembering
that we should retry and to not pass the error back to the panel driver.

I also don't want to introduce an error print here.

> > +	}
> > +
> > +	panel->backlight = backlight;
> > +	panel->backlight_init_pending = false;
> > +
> > +	return 0;
> > +}
> > +#else
> > +static int drm_panel_of_backlight_lazy(struct drm_panel *panel) { return 0; }
> > +#endif
> > +
> >  /**
> >   * drm_panel_init - initialize a panel
> >   * @panel: DRM panel
> > @@ -161,6 +189,9 @@ int drm_panel_enable(struct drm_panel *panel)
> >  			return ret;
> >  	}
> >  
> > +	if (panel->backlight_init_pending)
> > +		drm_panel_of_backlight_lazy(panel);
> > +
> >  	ret = backlight_enable(panel->backlight);
> >  	if (ret < 0)
> >  		DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
> > @@ -187,6 +218,9 @@ int drm_panel_disable(struct drm_panel *panel)
> >  	if (!panel)
> >  		return -EINVAL;
> >  
> > +	if (panel->backlight_init_pending)
> > +		drm_panel_of_backlight_lazy(panel);
> > +
> >  	ret = backlight_disable(panel->backlight);
> >  	if (ret < 0)
> >  		DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
> > @@ -328,18 +362,7 @@ EXPORT_SYMBOL(of_drm_get_panel_orientation);
> >   */
> >  int drm_panel_of_backlight(struct drm_panel *panel)
> >  {
> > -	struct backlight_device *backlight;
> > -
> > -	if (!panel || !panel->dev)
> > -		return -EINVAL;
> > -
> > -	backlight = devm_of_find_backlight(panel->dev);
> > -
> > -	if (IS_ERR(backlight))
> > -		return PTR_ERR(backlight);
> > -
> > -	panel->backlight = backlight;
> > -	return 0;
> > +	return drm_panel_of_backlight_lazy(panel);
> Could you update the drm_panel_of_backlight() implementation (and
> do not forget the documentation) and avoid drm_panel_of_backlight_lazy()?
> 

That sounds reasonable, there's not really a reason for introducing a
new function for what I'm doing.

> 
> >  }
> >  EXPORT_SYMBOL(drm_panel_of_backlight);
> >  #endif
> > diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
> > index 33605c3f0eba..b126abebb2f3 100644
> > --- a/include/drm/drm_panel.h
> > +++ b/include/drm/drm_panel.h
> > @@ -149,6 +149,14 @@ struct drm_panel {
> >  	 */
> >  	struct backlight_device *backlight;
> >  
> > +	/**
> > +	 * @backlight_init_pending
> > +	 *
> > +	 * Backlight driver is not yet available so further attempts to
> > +	 * initialize @backlight is necessary.
> > +	 */
> > +	bool backlight_init_pending;
> > +
> 
> We have not done so today for other fields, but it would be good
> to document this is for drm_panel use only and drivers shall not touch.
> 

Of course.

Thanks,
Bjorn
Bjorn Andersson Dec. 8, 2020, 10:02 p.m. UTC | #3
On Tue 08 Dec 06:47 CST 2020, Thierry Reding wrote:

> On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> > Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> > means of generating a PWM signal for backlight control of the attached
> > panel. The provided PWM chip is typically controlled by the
> > pwm-backlight driver, which if tied to the panel will provide DPMS.
> > 
> > But with the current implementation the panel will refuse to probe
> > because the bridge driver has yet to probe and register the PWM chip,
> > and the bridge driver will refuse to probe because it's unable to find
> > the panel.
> 
> What you're describing is basically a circular dependency. Can't we get
> rid of that in some other way? Why exactly does the bridge driver refuse
> to probe if the panel can't be found?
> 
> In other words, I see how the bridge would /use/ the panel in that it
> forward a video stream to it. But how does the panel /use/ the bridge?
> 

Yes, this is indeed a circular dependency between the components.

The involved parts are:
* the bridge driver that implements the PWM chip probe defers on
  drm_of_find_panel_or_bridge() failing to find the panel.
* the pwm-backlight driver that consumes the PWM channel probe defer
  because the pwm_chip was not registered by the bridge.
* the panel that uses the backlight for DPMS purposes probe defer
  because drm_panel_of_backlight() fails to find the pwm-backlight.

I looked at means of postponing drm_of_find_panel_or_bridge() to
drm_bridge_funcs->attach(), but at that time "deferral" would be fatal.
I looked at registering the pwm_chip earlier, but that would depend on a
guarantee of the pwm-backlight and panel driver to probe concurrently.
And the current solution of not tying the backlight to the panel means
that when userspace decides to DPMS the display the backlight stays on.


The proposed solution (hack?) means that DPMS operations happening
before the pwm-backlight has probed will be missed, so it's not perfect.
It does however allow the backlight on my laptop to turn off, which is a
big improvement.

But I'm certainly welcome to suggestions.

Regards,
Bjorn
Daniel Vetter Dec. 8, 2020, 11:52 p.m. UTC | #4
On Tue, Dec 08, 2020 at 04:02:16PM -0600, Bjorn Andersson wrote:
> On Tue 08 Dec 06:47 CST 2020, Thierry Reding wrote:
> 
> > On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> > > Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> > > means of generating a PWM signal for backlight control of the attached
> > > panel. The provided PWM chip is typically controlled by the
> > > pwm-backlight driver, which if tied to the panel will provide DPMS.
> > > 
> > > But with the current implementation the panel will refuse to probe
> > > because the bridge driver has yet to probe and register the PWM chip,
> > > and the bridge driver will refuse to probe because it's unable to find
> > > the panel.
> > 
> > What you're describing is basically a circular dependency. Can't we get
> > rid of that in some other way? Why exactly does the bridge driver refuse
> > to probe if the panel can't be found?
> > 
> > In other words, I see how the bridge would /use/ the panel in that it
> > forward a video stream to it. But how does the panel /use/ the bridge?
> > 
> 
> Yes, this is indeed a circular dependency between the components.
> 
> The involved parts are:
> * the bridge driver that implements the PWM chip probe defers on
>   drm_of_find_panel_or_bridge() failing to find the panel.
> * the pwm-backlight driver that consumes the PWM channel probe defer
>   because the pwm_chip was not registered by the bridge.
> * the panel that uses the backlight for DPMS purposes probe defer
>   because drm_panel_of_backlight() fails to find the pwm-backlight.
> 
> I looked at means of postponing drm_of_find_panel_or_bridge() to
> drm_bridge_funcs->attach(), but at that time "deferral" would be fatal.
> I looked at registering the pwm_chip earlier, but that would depend on a
> guarantee of the pwm-backlight and panel driver to probe concurrently.
> And the current solution of not tying the backlight to the panel means
> that when userspace decides to DPMS the display the backlight stays on.
> 
> 
> The proposed solution (hack?) means that DPMS operations happening
> before the pwm-backlight has probed will be missed, so it's not perfect.
> It does however allow the backlight on my laptop to turn off, which is a
> big improvement.
> 
> But I'm certainly welcome to suggestions.

Entirely hand-waving, why doesn't the following work:

1. driver for the platform device which is the bridge loads
2. that platform driver registers the pwm
3. it registers some magic for later on (more below)
4. panel driver has deferred loading until step 2 happened
5. panel driver registers drm_panel
6. the magic from step 3 picks up (after having been deferred for a few
times probably) grabs the panel, and sets up the actual drm_bridge driver

Everyone happy, or not? From the description it looks like the problem
that the pwm that we need for the backlight is tied to the same driver as
the drm_bridge, and always torn down too if the drm_bridge setup fails
somehow for a reason. And that reason is the circular dependency this
creates.

Now for the magic in step 3, there's options:
- change DT to split out that pwm as a separate platform_device, that way
  bridge and panel can load indepedently (hopefully)

- convert bridge to a multi-function device (mfd), essentially a way to
  instantiate more devices with their drivers at runtime. Then the actual
  pwm and drm_bridge parts of your bridge driver bind against those
  sub-functions, and can defer indepedently

- we could create a callback/wait function for "pls wait for any panel to
  show up". Then your bridge driver could launch a work_struct with that
  wait function, which will do the bridge setup once the panel has shown
  up. The pwm will be registered right away. It's essentially hand-rolling
  EPROBE_DEFERRED for work_struct in drm/panel. Maybe we might even have
  that exported from the driver core, e.g.

register_bridge_fn(struct work *)
{
	do_wait_probe_defer();
	panel = drm_of_find_panel_or_bridge();
	if (!panel) {
		schedule_work(); /* want to restart the work so it can be stopped on driver unload */
		return;
	}

	/* we have the panel now, register drm_bridge */
}

- cobble something together with component.c, but that's more for
  collecting unrelated struct device into a logical one than splitting it
  up more.

tldr; I think you can split this loop here at the bridge by untangling the
pwm from the drm_bridge part sufficiently.
-Daniel
Bjorn Andersson Dec. 9, 2020, 8:28 p.m. UTC | #5
On Tue 08 Dec 17:52 CST 2020, Daniel Vetter wrote:

> On Tue, Dec 08, 2020 at 04:02:16PM -0600, Bjorn Andersson wrote:
> > On Tue 08 Dec 06:47 CST 2020, Thierry Reding wrote:
> > 
> > > On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> > > > Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> > > > means of generating a PWM signal for backlight control of the attached
> > > > panel. The provided PWM chip is typically controlled by the
> > > > pwm-backlight driver, which if tied to the panel will provide DPMS.
> > > > 
> > > > But with the current implementation the panel will refuse to probe
> > > > because the bridge driver has yet to probe and register the PWM chip,
> > > > and the bridge driver will refuse to probe because it's unable to find
> > > > the panel.
> > > 
> > > What you're describing is basically a circular dependency. Can't we get
> > > rid of that in some other way? Why exactly does the bridge driver refuse
> > > to probe if the panel can't be found?
> > > 
> > > In other words, I see how the bridge would /use/ the panel in that it
> > > forward a video stream to it. But how does the panel /use/ the bridge?
> > > 
> > 
> > Yes, this is indeed a circular dependency between the components.
> > 
> > The involved parts are:
> > * the bridge driver that implements the PWM chip probe defers on
> >   drm_of_find_panel_or_bridge() failing to find the panel.
> > * the pwm-backlight driver that consumes the PWM channel probe defer
> >   because the pwm_chip was not registered by the bridge.
> > * the panel that uses the backlight for DPMS purposes probe defer
> >   because drm_panel_of_backlight() fails to find the pwm-backlight.
> > 
> > I looked at means of postponing drm_of_find_panel_or_bridge() to
> > drm_bridge_funcs->attach(), but at that time "deferral" would be fatal.
> > I looked at registering the pwm_chip earlier, but that would depend on a
> > guarantee of the pwm-backlight and panel driver to probe concurrently.
> > And the current solution of not tying the backlight to the panel means
> > that when userspace decides to DPMS the display the backlight stays on.
> > 
> > 
> > The proposed solution (hack?) means that DPMS operations happening
> > before the pwm-backlight has probed will be missed, so it's not perfect.
> > It does however allow the backlight on my laptop to turn off, which is a
> > big improvement.
> > 
> > But I'm certainly welcome to suggestions.
> 
> Entirely hand-waving, why doesn't the following work:
> 
> 1. driver for the platform device which is the bridge loads
> 2. that platform driver registers the pwm
> 3. it registers some magic for later on (more below)
> 4. panel driver has deferred loading until step 2 happened
> 5. panel driver registers drm_panel
> 6. the magic from step 3 picks up (after having been deferred for a few
> times probably) grabs the panel, and sets up the actual drm_bridge driver
> 
> Everyone happy, or not? From the description it looks like the problem
> that the pwm that we need for the backlight is tied to the same driver as
> the drm_bridge, and always torn down too if the drm_bridge setup fails
> somehow for a reason. And that reason is the circular dependency this
> creates.
> 
> Now for the magic in step 3, there's options:
> - change DT to split out that pwm as a separate platform_device, that way
>   bridge and panel can load indepedently (hopefully)
> 

This is an i2c device, so describing it multiple times would mean we
have multiple devices with the same address...

> - convert bridge to a multi-function device (mfd), essentially a way to
>   instantiate more devices with their drivers at runtime. Then the actual
>   pwm and drm_bridge parts of your bridge driver bind against those
>   sub-functions, and can defer indepedently
> 

But, this sounds reasonable and would rely on the existing probe
deferral logic and if there's ever any improvements in this area we
would directly benefit from it.

> - we could create a callback/wait function for "pls wait for any panel to
>   show up". Then your bridge driver could launch a work_struct with that
>   wait function, which will do the bridge setup once the panel has shown
>   up. The pwm will be registered right away. It's essentially hand-rolling
>   EPROBE_DEFERRED for work_struct in drm/panel. Maybe we might even have
>   that exported from the driver core, e.g.
> 
> register_bridge_fn(struct work *)
> {
> 	do_wait_probe_defer();
> 	panel = drm_of_find_panel_or_bridge();
> 	if (!panel) {
> 		schedule_work(); /* want to restart the work so it can be stopped on driver unload */
> 		return;
> 	}
> 
> 	/* we have the panel now, register drm_bridge */
> }
> 
> - cobble something together with component.c, but that's more for
>   collecting unrelated struct device into a logical one than splitting it
>   up more.
> 
> tldr; I think you can split this loop here at the bridge by untangling the
> pwm from the drm_bridge part sufficiently.

Yes, it seems like a reasonable path forward. But I wanted some input
before refactoring the whole thing.

Thank you,
Bjorn
Daniel Vetter Dec. 10, 2020, 10:15 a.m. UTC | #6
On Wed, Dec 09, 2020 at 02:28:18PM -0600, Bjorn Andersson wrote:
> On Tue 08 Dec 17:52 CST 2020, Daniel Vetter wrote:
> 
> > On Tue, Dec 08, 2020 at 04:02:16PM -0600, Bjorn Andersson wrote:
> > > On Tue 08 Dec 06:47 CST 2020, Thierry Reding wrote:
> > > 
> > > > On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> > > > > Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> > > > > means of generating a PWM signal for backlight control of the attached
> > > > > panel. The provided PWM chip is typically controlled by the
> > > > > pwm-backlight driver, which if tied to the panel will provide DPMS.
> > > > > 
> > > > > But with the current implementation the panel will refuse to probe
> > > > > because the bridge driver has yet to probe and register the PWM chip,
> > > > > and the bridge driver will refuse to probe because it's unable to find
> > > > > the panel.
> > > > 
> > > > What you're describing is basically a circular dependency. Can't we get
> > > > rid of that in some other way? Why exactly does the bridge driver refuse
> > > > to probe if the panel can't be found?
> > > > 
> > > > In other words, I see how the bridge would /use/ the panel in that it
> > > > forward a video stream to it. But how does the panel /use/ the bridge?
> > > > 
> > > 
> > > Yes, this is indeed a circular dependency between the components.
> > > 
> > > The involved parts are:
> > > * the bridge driver that implements the PWM chip probe defers on
> > >   drm_of_find_panel_or_bridge() failing to find the panel.
> > > * the pwm-backlight driver that consumes the PWM channel probe defer
> > >   because the pwm_chip was not registered by the bridge.
> > > * the panel that uses the backlight for DPMS purposes probe defer
> > >   because drm_panel_of_backlight() fails to find the pwm-backlight.
> > > 
> > > I looked at means of postponing drm_of_find_panel_or_bridge() to
> > > drm_bridge_funcs->attach(), but at that time "deferral" would be fatal.
> > > I looked at registering the pwm_chip earlier, but that would depend on a
> > > guarantee of the pwm-backlight and panel driver to probe concurrently.
> > > And the current solution of not tying the backlight to the panel means
> > > that when userspace decides to DPMS the display the backlight stays on.
> > > 
> > > 
> > > The proposed solution (hack?) means that DPMS operations happening
> > > before the pwm-backlight has probed will be missed, so it's not perfect.
> > > It does however allow the backlight on my laptop to turn off, which is a
> > > big improvement.
> > > 
> > > But I'm certainly welcome to suggestions.
> > 
> > Entirely hand-waving, why doesn't the following work:
> > 
> > 1. driver for the platform device which is the bridge loads
> > 2. that platform driver registers the pwm
> > 3. it registers some magic for later on (more below)
> > 4. panel driver has deferred loading until step 2 happened
> > 5. panel driver registers drm_panel
> > 6. the magic from step 3 picks up (after having been deferred for a few
> > times probably) grabs the panel, and sets up the actual drm_bridge driver
> > 
> > Everyone happy, or not? From the description it looks like the problem
> > that the pwm that we need for the backlight is tied to the same driver as
> > the drm_bridge, and always torn down too if the drm_bridge setup fails
> > somehow for a reason. And that reason is the circular dependency this
> > creates.
> > 
> > Now for the magic in step 3, there's options:
> > - change DT to split out that pwm as a separate platform_device, that way
> >   bridge and panel can load indepedently (hopefully)
> > 
> 
> This is an i2c device, so describing it multiple times would mean we
> have multiple devices with the same address...
> 
> > - convert bridge to a multi-function device (mfd), essentially a way to
> >   instantiate more devices with their drivers at runtime. Then the actual
> >   pwm and drm_bridge parts of your bridge driver bind against those
> >   sub-functions, and can defer indepedently
> > 
> 
> But, this sounds reasonable and would rely on the existing probe
> deferral logic and if there's ever any improvements in this area we
> would directly benefit from it.
> 
> > - we could create a callback/wait function for "pls wait for any panel to
> >   show up". Then your bridge driver could launch a work_struct with that
> >   wait function, which will do the bridge setup once the panel has shown
> >   up. The pwm will be registered right away. It's essentially hand-rolling
> >   EPROBE_DEFERRED for work_struct in drm/panel. Maybe we might even have
> >   that exported from the driver core, e.g.
> > 
> > register_bridge_fn(struct work *)
> > {
> > 	do_wait_probe_defer();
> > 	panel = drm_of_find_panel_or_bridge();
> > 	if (!panel) {
> > 		schedule_work(); /* want to restart the work so it can be stopped on driver unload */
> > 		return;
> > 	}
> > 
> > 	/* we have the panel now, register drm_bridge */
> > }
> > 
> > - cobble something together with component.c, but that's more for
> >   collecting unrelated struct device into a logical one than splitting it
> >   up more.
> > 
> > tldr; I think you can split this loop here at the bridge by untangling the
> > pwm from the drm_bridge part sufficiently.
> 
> Yes, it seems like a reasonable path forward. But I wanted some input
> before refactoring the whole thing.

Yeah it's unfortunately a bit of work. But I think it's the proper
approach since EPROBE_DEFERRED is fundamentally linked to struct device
and bound drivers. So we do need a struct device for every part in our
dependency graph to make sure we can resolve the dependencies all
correctly with reprobing.
-Daniel
Thierry Reding Dec. 10, 2020, 4:44 p.m. UTC | #7
On Thu, Dec 10, 2020 at 11:15:38AM +0100, Daniel Vetter wrote:
> On Wed, Dec 09, 2020 at 02:28:18PM -0600, Bjorn Andersson wrote:
> > On Tue 08 Dec 17:52 CST 2020, Daniel Vetter wrote:
> > 
> > > On Tue, Dec 08, 2020 at 04:02:16PM -0600, Bjorn Andersson wrote:
> > > > On Tue 08 Dec 06:47 CST 2020, Thierry Reding wrote:
> > > > 
> > > > > On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> > > > > > Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> > > > > > means of generating a PWM signal for backlight control of the attached
> > > > > > panel. The provided PWM chip is typically controlled by the
> > > > > > pwm-backlight driver, which if tied to the panel will provide DPMS.
> > > > > > 
> > > > > > But with the current implementation the panel will refuse to probe
> > > > > > because the bridge driver has yet to probe and register the PWM chip,
> > > > > > and the bridge driver will refuse to probe because it's unable to find
> > > > > > the panel.
> > > > > 
> > > > > What you're describing is basically a circular dependency. Can't we get
> > > > > rid of that in some other way? Why exactly does the bridge driver refuse
> > > > > to probe if the panel can't be found?
> > > > > 
> > > > > In other words, I see how the bridge would /use/ the panel in that it
> > > > > forward a video stream to it. But how does the panel /use/ the bridge?
> > > > > 
> > > > 
> > > > Yes, this is indeed a circular dependency between the components.
> > > > 
> > > > The involved parts are:
> > > > * the bridge driver that implements the PWM chip probe defers on
> > > >   drm_of_find_panel_or_bridge() failing to find the panel.
> > > > * the pwm-backlight driver that consumes the PWM channel probe defer
> > > >   because the pwm_chip was not registered by the bridge.
> > > > * the panel that uses the backlight for DPMS purposes probe defer
> > > >   because drm_panel_of_backlight() fails to find the pwm-backlight.
> > > > 
> > > > I looked at means of postponing drm_of_find_panel_or_bridge() to
> > > > drm_bridge_funcs->attach(), but at that time "deferral" would be fatal.
> > > > I looked at registering the pwm_chip earlier, but that would depend on a
> > > > guarantee of the pwm-backlight and panel driver to probe concurrently.
> > > > And the current solution of not tying the backlight to the panel means
> > > > that when userspace decides to DPMS the display the backlight stays on.
> > > > 
> > > > 
> > > > The proposed solution (hack?) means that DPMS operations happening
> > > > before the pwm-backlight has probed will be missed, so it's not perfect.
> > > > It does however allow the backlight on my laptop to turn off, which is a
> > > > big improvement.
> > > > 
> > > > But I'm certainly welcome to suggestions.
> > > 
> > > Entirely hand-waving, why doesn't the following work:
> > > 
> > > 1. driver for the platform device which is the bridge loads
> > > 2. that platform driver registers the pwm
> > > 3. it registers some magic for later on (more below)
> > > 4. panel driver has deferred loading until step 2 happened
> > > 5. panel driver registers drm_panel
> > > 6. the magic from step 3 picks up (after having been deferred for a few
> > > times probably) grabs the panel, and sets up the actual drm_bridge driver
> > > 
> > > Everyone happy, or not? From the description it looks like the problem
> > > that the pwm that we need for the backlight is tied to the same driver as
> > > the drm_bridge, and always torn down too if the drm_bridge setup fails
> > > somehow for a reason. And that reason is the circular dependency this
> > > creates.
> > > 
> > > Now for the magic in step 3, there's options:
> > > - change DT to split out that pwm as a separate platform_device, that way
> > >   bridge and panel can load indepedently (hopefully)
> > > 
> > 
> > This is an i2c device, so describing it multiple times would mean we
> > have multiple devices with the same address...
> > 
> > > - convert bridge to a multi-function device (mfd), essentially a way to
> > >   instantiate more devices with their drivers at runtime. Then the actual
> > >   pwm and drm_bridge parts of your bridge driver bind against those
> > >   sub-functions, and can defer indepedently
> > > 
> > 
> > But, this sounds reasonable and would rely on the existing probe
> > deferral logic and if there's ever any improvements in this area we
> > would directly benefit from it.
> > 
> > > - we could create a callback/wait function for "pls wait for any panel to
> > >   show up". Then your bridge driver could launch a work_struct with that
> > >   wait function, which will do the bridge setup once the panel has shown
> > >   up. The pwm will be registered right away. It's essentially hand-rolling
> > >   EPROBE_DEFERRED for work_struct in drm/panel. Maybe we might even have
> > >   that exported from the driver core, e.g.
> > > 
> > > register_bridge_fn(struct work *)
> > > {
> > > 	do_wait_probe_defer();
> > > 	panel = drm_of_find_panel_or_bridge();
> > > 	if (!panel) {
> > > 		schedule_work(); /* want to restart the work so it can be stopped on driver unload */
> > > 		return;
> > > 	}
> > > 
> > > 	/* we have the panel now, register drm_bridge */
> > > }
> > > 
> > > - cobble something together with component.c, but that's more for
> > >   collecting unrelated struct device into a logical one than splitting it
> > >   up more.
> > > 
> > > tldr; I think you can split this loop here at the bridge by untangling the
> > > pwm from the drm_bridge part sufficiently.
> > 
> > Yes, it seems like a reasonable path forward. But I wanted some input
> > before refactoring the whole thing.
> 
> Yeah it's unfortunately a bit of work. But I think it's the proper
> approach since EPROBE_DEFERRED is fundamentally linked to struct device
> and bound drivers. So we do need a struct device for every part in our
> dependency graph to make sure we can resolve the dependencies all
> correctly with reprobing.

Can we not turn things around and make the bridge driver independent of
the panel? To me it sounds like these loosely follow a hierarchy where
it makes sense to probe the bridge first, without any dependency on the
panel that's being used. I think this makes sense because this example
shows that panels may depend on resources provided by the bridge. In
this case it's a backlight, but I could also imagine the bridge
providing some sort of I2C bus that the panel driver may need to use in
order to query the panel's EDID.

The way I imagine that this would work is that the panel would probe
separately from the bridge driver but use the OF graph to look up the
bridge that has the resources (backlight, I2C bus, ...) that it needs to
proceed. As long as that bridge has not been probed, the panel would
need to defer, which is the standard way that provider/consumer pairs
work. Once the bridge has probed, the panel can also proceed to probe
because it can now find the necessary resources.

The only missing thing that I don't think we have right now is a way for
the panel to then register with its parent bridge, but that should be
fairly easy to add. I suspect this might get a bit tricky around the
connector state paths because we can now get into a situation where the
connector can have a complete bridge path set up but may be missing the
panel (which I think in the current model can't happen because the
bridge always relies on the panel being there, although it sounds like
it could happen with Daniel's proposal as well). But that ultimately is
not very different from how we deal with monitors on more standard
interfaces like HDMI or DP where we emit a hotplug event when the
monitor becomes available, so perhaps that infrastructure could be
reused for this.

Thierry
Daniel Vetter Dec. 10, 2020, 4:50 p.m. UTC | #8
On Thu, Dec 10, 2020 at 5:44 PM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Thu, Dec 10, 2020 at 11:15:38AM +0100, Daniel Vetter wrote:
> > On Wed, Dec 09, 2020 at 02:28:18PM -0600, Bjorn Andersson wrote:
> > > On Tue 08 Dec 17:52 CST 2020, Daniel Vetter wrote:
> > >
> > > > On Tue, Dec 08, 2020 at 04:02:16PM -0600, Bjorn Andersson wrote:
> > > > > On Tue 08 Dec 06:47 CST 2020, Thierry Reding wrote:
> > > > >
> > > > > > On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> > > > > > > Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> > > > > > > means of generating a PWM signal for backlight control of the attached
> > > > > > > panel. The provided PWM chip is typically controlled by the
> > > > > > > pwm-backlight driver, which if tied to the panel will provide DPMS.
> > > > > > >
> > > > > > > But with the current implementation the panel will refuse to probe
> > > > > > > because the bridge driver has yet to probe and register the PWM chip,
> > > > > > > and the bridge driver will refuse to probe because it's unable to find
> > > > > > > the panel.
> > > > > >
> > > > > > What you're describing is basically a circular dependency. Can't we get
> > > > > > rid of that in some other way? Why exactly does the bridge driver refuse
> > > > > > to probe if the panel can't be found?
> > > > > >
> > > > > > In other words, I see how the bridge would /use/ the panel in that it
> > > > > > forward a video stream to it. But how does the panel /use/ the bridge?
> > > > > >
> > > > >
> > > > > Yes, this is indeed a circular dependency between the components.
> > > > >
> > > > > The involved parts are:
> > > > > * the bridge driver that implements the PWM chip probe defers on
> > > > >   drm_of_find_panel_or_bridge() failing to find the panel.
> > > > > * the pwm-backlight driver that consumes the PWM channel probe defer
> > > > >   because the pwm_chip was not registered by the bridge.
> > > > > * the panel that uses the backlight for DPMS purposes probe defer
> > > > >   because drm_panel_of_backlight() fails to find the pwm-backlight.
> > > > >
> > > > > I looked at means of postponing drm_of_find_panel_or_bridge() to
> > > > > drm_bridge_funcs->attach(), but at that time "deferral" would be fatal.
> > > > > I looked at registering the pwm_chip earlier, but that would depend on a
> > > > > guarantee of the pwm-backlight and panel driver to probe concurrently.
> > > > > And the current solution of not tying the backlight to the panel means
> > > > > that when userspace decides to DPMS the display the backlight stays on.
> > > > >
> > > > >
> > > > > The proposed solution (hack?) means that DPMS operations happening
> > > > > before the pwm-backlight has probed will be missed, so it's not perfect.
> > > > > It does however allow the backlight on my laptop to turn off, which is a
> > > > > big improvement.
> > > > >
> > > > > But I'm certainly welcome to suggestions.
> > > >
> > > > Entirely hand-waving, why doesn't the following work:
> > > >
> > > > 1. driver for the platform device which is the bridge loads
> > > > 2. that platform driver registers the pwm
> > > > 3. it registers some magic for later on (more below)
> > > > 4. panel driver has deferred loading until step 2 happened
> > > > 5. panel driver registers drm_panel
> > > > 6. the magic from step 3 picks up (after having been deferred for a few
> > > > times probably) grabs the panel, and sets up the actual drm_bridge driver
> > > >
> > > > Everyone happy, or not? From the description it looks like the problem
> > > > that the pwm that we need for the backlight is tied to the same driver as
> > > > the drm_bridge, and always torn down too if the drm_bridge setup fails
> > > > somehow for a reason. And that reason is the circular dependency this
> > > > creates.
> > > >
> > > > Now for the magic in step 3, there's options:
> > > > - change DT to split out that pwm as a separate platform_device, that way
> > > >   bridge and panel can load indepedently (hopefully)
> > > >
> > >
> > > This is an i2c device, so describing it multiple times would mean we
> > > have multiple devices with the same address...
> > >
> > > > - convert bridge to a multi-function device (mfd), essentially a way to
> > > >   instantiate more devices with their drivers at runtime. Then the actual
> > > >   pwm and drm_bridge parts of your bridge driver bind against those
> > > >   sub-functions, and can defer indepedently
> > > >
> > >
> > > But, this sounds reasonable and would rely on the existing probe
> > > deferral logic and if there's ever any improvements in this area we
> > > would directly benefit from it.
> > >
> > > > - we could create a callback/wait function for "pls wait for any panel to
> > > >   show up". Then your bridge driver could launch a work_struct with that
> > > >   wait function, which will do the bridge setup once the panel has shown
> > > >   up. The pwm will be registered right away. It's essentially hand-rolling
> > > >   EPROBE_DEFERRED for work_struct in drm/panel. Maybe we might even have
> > > >   that exported from the driver core, e.g.
> > > >
> > > > register_bridge_fn(struct work *)
> > > > {
> > > >   do_wait_probe_defer();
> > > >   panel = drm_of_find_panel_or_bridge();
> > > >   if (!panel) {
> > > >           schedule_work(); /* want to restart the work so it can be stopped on driver unload */
> > > >           return;
> > > >   }
> > > >
> > > >   /* we have the panel now, register drm_bridge */
> > > > }
> > > >
> > > > - cobble something together with component.c, but that's more for
> > > >   collecting unrelated struct device into a logical one than splitting it
> > > >   up more.
> > > >
> > > > tldr; I think you can split this loop here at the bridge by untangling the
> > > > pwm from the drm_bridge part sufficiently.
> > >
> > > Yes, it seems like a reasonable path forward. But I wanted some input
> > > before refactoring the whole thing.
> >
> > Yeah it's unfortunately a bit of work. But I think it's the proper
> > approach since EPROBE_DEFERRED is fundamentally linked to struct device
> > and bound drivers. So we do need a struct device for every part in our
> > dependency graph to make sure we can resolve the dependencies all
> > correctly with reprobing.
>
> Can we not turn things around and make the bridge driver independent of
> the panel? To me it sounds like these loosely follow a hierarchy where
> it makes sense to probe the bridge first, without any dependency on the
> panel that's being used. I think this makes sense because this example
> shows that panels may depend on resources provided by the bridge. In
> this case it's a backlight, but I could also imagine the bridge
> providing some sort of I2C bus that the panel driver may need to use in
> order to query the panel's EDID.
>
> The way I imagine that this would work is that the panel would probe
> separately from the bridge driver but use the OF graph to look up the
> bridge that has the resources (backlight, I2C bus, ...) that it needs to
> proceed. As long as that bridge has not been probed, the panel would
> need to defer, which is the standard way that provider/consumer pairs
> work. Once the bridge has probed, the panel can also proceed to probe
> because it can now find the necessary resources.

Yeah that might be the other option, treat the panel as a bridge (we
have the panel bridge already), then build up the entire thing as a
bridge chain. Not sure how much this is "just works" territory or not.

> The only missing thing that I don't think we have right now is a way for
> the panel to then register with its parent bridge, but that should be
> fairly easy to add. I suspect this might get a bit tricky around the
> connector state paths because we can now get into a situation where the
> connector can have a complete bridge path set up but may be missing the
> panel (which I think in the current model can't happen because the
> bridge always relies on the panel being there, although it sounds like
> it could happen with Daniel's proposal as well). But that ultimately is
> not very different from how we deal with monitors on more standard
> interfaces like HDMI or DP where we emit a hotplug event when the
> monitor becomes available, so perhaps that infrastructure could be
> reused for this.

Generally we try really hard to make sure panels are always there and
never hotplug in/out. Not sure whether there's even userspace/desktops
relying on this. So hotplugging the panel later on does not sound
good. Bjorn's patch here does a light version of that with the
backlight, and we're having this sprawling thread because that's bit
suboptimal - userspace could boot real fast, see there's no backlight,
and then not expose backlight adjusters if we're unlucky.
-Daniel
Thierry Reding Dec. 10, 2020, 5:18 p.m. UTC | #9
On Thu, Dec 10, 2020 at 05:50:00PM +0100, Daniel Vetter wrote:
> On Thu, Dec 10, 2020 at 5:44 PM Thierry Reding <thierry.reding@gmail.com> wrote:
> >
> > On Thu, Dec 10, 2020 at 11:15:38AM +0100, Daniel Vetter wrote:
> > > On Wed, Dec 09, 2020 at 02:28:18PM -0600, Bjorn Andersson wrote:
> > > > On Tue 08 Dec 17:52 CST 2020, Daniel Vetter wrote:
> > > >
> > > > > On Tue, Dec 08, 2020 at 04:02:16PM -0600, Bjorn Andersson wrote:
> > > > > > On Tue 08 Dec 06:47 CST 2020, Thierry Reding wrote:
> > > > > >
> > > > > > > On Mon, Dec 07, 2020 at 10:44:46PM -0600, Bjorn Andersson wrote:
> > > > > > > > Some bridge chips, such as the TI SN65DSI86 DSI/eDP bridge, provides
> > > > > > > > means of generating a PWM signal for backlight control of the attached
> > > > > > > > panel. The provided PWM chip is typically controlled by the
> > > > > > > > pwm-backlight driver, which if tied to the panel will provide DPMS.
> > > > > > > >
> > > > > > > > But with the current implementation the panel will refuse to probe
> > > > > > > > because the bridge driver has yet to probe and register the PWM chip,
> > > > > > > > and the bridge driver will refuse to probe because it's unable to find
> > > > > > > > the panel.
> > > > > > >
> > > > > > > What you're describing is basically a circular dependency. Can't we get
> > > > > > > rid of that in some other way? Why exactly does the bridge driver refuse
> > > > > > > to probe if the panel can't be found?
> > > > > > >
> > > > > > > In other words, I see how the bridge would /use/ the panel in that it
> > > > > > > forward a video stream to it. But how does the panel /use/ the bridge?
> > > > > > >
> > > > > >
> > > > > > Yes, this is indeed a circular dependency between the components.
> > > > > >
> > > > > > The involved parts are:
> > > > > > * the bridge driver that implements the PWM chip probe defers on
> > > > > >   drm_of_find_panel_or_bridge() failing to find the panel.
> > > > > > * the pwm-backlight driver that consumes the PWM channel probe defer
> > > > > >   because the pwm_chip was not registered by the bridge.
> > > > > > * the panel that uses the backlight for DPMS purposes probe defer
> > > > > >   because drm_panel_of_backlight() fails to find the pwm-backlight.
> > > > > >
> > > > > > I looked at means of postponing drm_of_find_panel_or_bridge() to
> > > > > > drm_bridge_funcs->attach(), but at that time "deferral" would be fatal.
> > > > > > I looked at registering the pwm_chip earlier, but that would depend on a
> > > > > > guarantee of the pwm-backlight and panel driver to probe concurrently.
> > > > > > And the current solution of not tying the backlight to the panel means
> > > > > > that when userspace decides to DPMS the display the backlight stays on.
> > > > > >
> > > > > >
> > > > > > The proposed solution (hack?) means that DPMS operations happening
> > > > > > before the pwm-backlight has probed will be missed, so it's not perfect.
> > > > > > It does however allow the backlight on my laptop to turn off, which is a
> > > > > > big improvement.
> > > > > >
> > > > > > But I'm certainly welcome to suggestions.
> > > > >
> > > > > Entirely hand-waving, why doesn't the following work:
> > > > >
> > > > > 1. driver for the platform device which is the bridge loads
> > > > > 2. that platform driver registers the pwm
> > > > > 3. it registers some magic for later on (more below)
> > > > > 4. panel driver has deferred loading until step 2 happened
> > > > > 5. panel driver registers drm_panel
> > > > > 6. the magic from step 3 picks up (after having been deferred for a few
> > > > > times probably) grabs the panel, and sets up the actual drm_bridge driver
> > > > >
> > > > > Everyone happy, or not? From the description it looks like the problem
> > > > > that the pwm that we need for the backlight is tied to the same driver as
> > > > > the drm_bridge, and always torn down too if the drm_bridge setup fails
> > > > > somehow for a reason. And that reason is the circular dependency this
> > > > > creates.
> > > > >
> > > > > Now for the magic in step 3, there's options:
> > > > > - change DT to split out that pwm as a separate platform_device, that way
> > > > >   bridge and panel can load indepedently (hopefully)
> > > > >
> > > >
> > > > This is an i2c device, so describing it multiple times would mean we
> > > > have multiple devices with the same address...
> > > >
> > > > > - convert bridge to a multi-function device (mfd), essentially a way to
> > > > >   instantiate more devices with their drivers at runtime. Then the actual
> > > > >   pwm and drm_bridge parts of your bridge driver bind against those
> > > > >   sub-functions, and can defer indepedently
> > > > >
> > > >
> > > > But, this sounds reasonable and would rely on the existing probe
> > > > deferral logic and if there's ever any improvements in this area we
> > > > would directly benefit from it.
> > > >
> > > > > - we could create a callback/wait function for "pls wait for any panel to
> > > > >   show up". Then your bridge driver could launch a work_struct with that
> > > > >   wait function, which will do the bridge setup once the panel has shown
> > > > >   up. The pwm will be registered right away. It's essentially hand-rolling
> > > > >   EPROBE_DEFERRED for work_struct in drm/panel. Maybe we might even have
> > > > >   that exported from the driver core, e.g.
> > > > >
> > > > > register_bridge_fn(struct work *)
> > > > > {
> > > > >   do_wait_probe_defer();
> > > > >   panel = drm_of_find_panel_or_bridge();
> > > > >   if (!panel) {
> > > > >           schedule_work(); /* want to restart the work so it can be stopped on driver unload */
> > > > >           return;
> > > > >   }
> > > > >
> > > > >   /* we have the panel now, register drm_bridge */
> > > > > }
> > > > >
> > > > > - cobble something together with component.c, but that's more for
> > > > >   collecting unrelated struct device into a logical one than splitting it
> > > > >   up more.
> > > > >
> > > > > tldr; I think you can split this loop here at the bridge by untangling the
> > > > > pwm from the drm_bridge part sufficiently.
> > > >
> > > > Yes, it seems like a reasonable path forward. But I wanted some input
> > > > before refactoring the whole thing.
> > >
> > > Yeah it's unfortunately a bit of work. But I think it's the proper
> > > approach since EPROBE_DEFERRED is fundamentally linked to struct device
> > > and bound drivers. So we do need a struct device for every part in our
> > > dependency graph to make sure we can resolve the dependencies all
> > > correctly with reprobing.
> >
> > Can we not turn things around and make the bridge driver independent of
> > the panel? To me it sounds like these loosely follow a hierarchy where
> > it makes sense to probe the bridge first, without any dependency on the
> > panel that's being used. I think this makes sense because this example
> > shows that panels may depend on resources provided by the bridge. In
> > this case it's a backlight, but I could also imagine the bridge
> > providing some sort of I2C bus that the panel driver may need to use in
> > order to query the panel's EDID.
> >
> > The way I imagine that this would work is that the panel would probe
> > separately from the bridge driver but use the OF graph to look up the
> > bridge that has the resources (backlight, I2C bus, ...) that it needs to
> > proceed. As long as that bridge has not been probed, the panel would
> > need to defer, which is the standard way that provider/consumer pairs
> > work. Once the bridge has probed, the panel can also proceed to probe
> > because it can now find the necessary resources.
> 
> Yeah that might be the other option, treat the panel as a bridge (we
> have the panel bridge already), then build up the entire thing as a
> bridge chain. Not sure how much this is "just works" territory or not.
> 
> > The only missing thing that I don't think we have right now is a way for
> > the panel to then register with its parent bridge, but that should be
> > fairly easy to add. I suspect this might get a bit tricky around the
> > connector state paths because we can now get into a situation where the
> > connector can have a complete bridge path set up but may be missing the
> > panel (which I think in the current model can't happen because the
> > bridge always relies on the panel being there, although it sounds like
> > it could happen with Daniel's proposal as well). But that ultimately is
> > not very different from how we deal with monitors on more standard
> > interfaces like HDMI or DP where we emit a hotplug event when the
> > monitor becomes available, so perhaps that infrastructure could be
> > reused for this.
> 
> Generally we try really hard to make sure panels are always there and
> never hotplug in/out. Not sure whether there's even userspace/desktops
> relying on this. So hotplugging the panel later on does not sound
> good. Bjorn's patch here does a light version of that with the
> backlight, and we're having this sprawling thread because that's bit
> suboptimal - userspace could boot real fast, see there's no backlight,
> and then not expose backlight adjusters if we're unlucky.

Yeah, hotplugging a panel would certainly be unusual. But I have never
come across userspace that distinguishes by connector type in how it
deals with hotplug events, so this might just work.

Irrespective of that, it shouldn't be necessary to hotplug panels. We
could always have the connector driver defer probe as long as the
bridge/panel chain has not been fully established. That way the whole
DRM device wouldn't show up until the panel has been connected, so it
should never be necessary to hotplug the panel in that case.

Thierry
diff mbox series

Patch

diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index f634371c717a..7487329bd22d 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -43,6 +43,34 @@  static LIST_HEAD(panel_list);
  * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add().
  */
 
+#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
+static int drm_panel_of_backlight_lazy(struct drm_panel *panel)
+{
+	struct backlight_device *backlight;
+
+	if (!panel || !panel->dev)
+		return -EINVAL;
+
+	backlight = devm_of_find_backlight(panel->dev);
+
+	if (IS_ERR(backlight)) {
+		if (PTR_ERR(backlight) == -EPROBE_DEFER) {
+			panel->backlight_init_pending = true;
+			return 0;
+		}
+
+		return PTR_ERR(backlight);
+	}
+
+	panel->backlight = backlight;
+	panel->backlight_init_pending = false;
+
+	return 0;
+}
+#else
+static int drm_panel_of_backlight_lazy(struct drm_panel *panel) { return 0; }
+#endif
+
 /**
  * drm_panel_init - initialize a panel
  * @panel: DRM panel
@@ -161,6 +189,9 @@  int drm_panel_enable(struct drm_panel *panel)
 			return ret;
 	}
 
+	if (panel->backlight_init_pending)
+		drm_panel_of_backlight_lazy(panel);
+
 	ret = backlight_enable(panel->backlight);
 	if (ret < 0)
 		DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
@@ -187,6 +218,9 @@  int drm_panel_disable(struct drm_panel *panel)
 	if (!panel)
 		return -EINVAL;
 
+	if (panel->backlight_init_pending)
+		drm_panel_of_backlight_lazy(panel);
+
 	ret = backlight_disable(panel->backlight);
 	if (ret < 0)
 		DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
@@ -328,18 +362,7 @@  EXPORT_SYMBOL(of_drm_get_panel_orientation);
  */
 int drm_panel_of_backlight(struct drm_panel *panel)
 {
-	struct backlight_device *backlight;
-
-	if (!panel || !panel->dev)
-		return -EINVAL;
-
-	backlight = devm_of_find_backlight(panel->dev);
-
-	if (IS_ERR(backlight))
-		return PTR_ERR(backlight);
-
-	panel->backlight = backlight;
-	return 0;
+	return drm_panel_of_backlight_lazy(panel);
 }
 EXPORT_SYMBOL(drm_panel_of_backlight);
 #endif
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 33605c3f0eba..b126abebb2f3 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -149,6 +149,14 @@  struct drm_panel {
 	 */
 	struct backlight_device *backlight;
 
+	/**
+	 * @backlight_init_pending
+	 *
+	 * Backlight driver is not yet available so further attempts to
+	 * initialize @backlight is necessary.
+	 */
+	bool backlight_init_pending;
+
 	/**
 	 * @funcs:
 	 *