[PATCH v3 2/2] drm/i915/display: Prevent DC6 while vblank is enabled for Panel Replay

Hogander, Jouni jouni.hogander at intel.com
Wed Sep 18 06:07:05 UTC 2024


On Tue, 2024-09-17 at 21:15 +0300, Ville Syrjälä wrote:
> On Tue, Sep 17, 2024 at 09:36:00AM +0300, Jouni Högander wrote:
> > We need to block DC6 entry in case of Panel Replay as enabling VBI
> > doesn't
> > prevent DC6 in case of Panel Replay. This causes problems if user-
> > space is
> > polling for vblank events.
> > 
> > Fix this by setting target DC state as DC_STATE_EN_UPTO_DC5 when
> > both
> > source and sink are supporting eDP Panel Replay and VBI is enabled.
> > 
> > v2:
> >   - use READ_ONCE in intel_display_vblank_work
> >   - use DC_STATE_DISABLE instead of DC_STATE_EN_UPTO_DC6
> >   - use intel_crtc->block_dc6_needed
> > 
> > Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/2296
> > Signed-off-by: Jouni Högander <jouni.hogander at intel.com>
> > ---
> >  .../gpu/drm/i915/display/intel_display_core.h |  2 ++
> >  .../gpu/drm/i915/display/intel_display_irq.c  | 28
> > +++++++++++++++++++
> >  2 files changed, 30 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h
> > b/drivers/gpu/drm/i915/display/intel_display_core.h
> > index 0a711114ff2b4..0707bc2047931 100644
> > --- a/drivers/gpu/drm/i915/display/intel_display_core.h
> > +++ b/drivers/gpu/drm/i915/display/intel_display_core.h
> > @@ -457,6 +457,8 @@ struct intel_display {
> >                 /* For i915gm/i945gm vblank irq workaround */
> >                 u8 vblank_enabled;
> >  
> > +               struct work_struct vblank_work;
> > +
> >                 u32 de_irq_mask[I915_MAX_PIPES];
> >                 u32 pipestat_irq_mask[I915_MAX_PIPES];
> >         } irq;
> > diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c
> > b/drivers/gpu/drm/i915/display/intel_display_irq.c
> > index 8f13f148c73e3..4bdc67e1baa31 100644
> > --- a/drivers/gpu/drm/i915/display/intel_display_irq.c
> > +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c
> > @@ -1361,16 +1361,38 @@ static bool gen11_dsi_configure_te(struct
> > intel_crtc *intel_crtc,
> >         return true;
> >  }
> >  
> > +static void intel_display_vblank_work(struct work_struct *work)
> > +{
> > +       struct intel_display *display =
> > +               container_of(work, typeof(*display),
> > irq.vblank_work);
> > +       struct drm_i915_private *i915 = to_i915(display->drm);
> > +       u8 vblank_enabled = READ_ONCE(display->irq.vblank_enabled);
> 
> Could be a bool since you don't use the numeric value for anything.
> Or could just not have a local variable since you only use it once
> anyway.

I will change this.
> 
> > +
> > +       /*
> > +        * NOTE: intel_display_power_set_target_dc_state is used
> > only by PSR
> > +        * code for DC3CO handling. DC3CO target state is currently
> > disabled in
> > +        * PSR code. If DC3CO is taken into use we need take that
> > into account
> > +        * here as well.
> > +        */
> > +       intel_display_power_set_target_dc_state(i915,
> > vblank_enabled ? DC_STATE_DISABLE :
> > +                                               DC_STATE_EN_UPTO_DC
> > 6);
> > +}
> > +
> >  int bdw_enable_vblank(struct drm_crtc *_crtc)
> >  {
> >         struct intel_crtc *crtc = to_intel_crtc(_crtc);
> > +       struct intel_display *display = to_intel_display(crtc);
> >         struct drm_i915_private *dev_priv = to_i915(crtc-
> > >base.dev);
> >         enum pipe pipe = crtc->pipe;
> >         unsigned long irqflags;
> > +       u8 block_dc6_needed = READ_ONCE(crtc->block_dc6_needed);
> 
> This doesn't really need the read once dance IMO since this
> will never change between vblank on/off.
> 
> Feels like the introduction of that flag should also be part of
> this patch, and this should be the first patch, and the second
> patch would then just figure out when to set said flag.

Ok, I will change this.
> 
> >  
> >         if (gen11_dsi_configure_te(crtc, true))
> >                 return 0;
> >  
> > +       if (display->irq.vblank_enabled++ == 0 && block_dc6_needed)
> > +               schedule_work(&display->irq.vblank_work);
> > +
> >         spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
> >         bdw_enable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK);
> >         spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
> > @@ -1436,6 +1458,7 @@ void ilk_disable_vblank(struct drm_crtc
> > *crtc)
> >  void bdw_disable_vblank(struct drm_crtc *_crtc)
> >  {
> >         struct intel_crtc *crtc = to_intel_crtc(_crtc);
> > +       struct intel_display *display = to_intel_display(crtc);
> >         struct drm_i915_private *dev_priv = to_i915(crtc-
> > >base.dev);
> >         enum pipe pipe = crtc->pipe;
> >         unsigned long irqflags;
> > @@ -1446,6 +1469,9 @@ void bdw_disable_vblank(struct drm_crtc
> > *_crtc)
> >         spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
> >         bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK);
> >         spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
> > +
> > +       if (--display->irq.vblank_enabled == 0)
> 
> This one seems to be missing the block_dc6_needed check.

Yes, that was left out as in version 2 block_dc6_needed was reference
count. I will add it.

> 
> > +               schedule_work(&display->irq.vblank_work);
> >  }
> >  
> >  void vlv_display_irq_reset(struct drm_i915_private *dev_priv)
> > @@ -1871,4 +1897,6 @@ void intel_display_irq_init(struct
> > drm_i915_private *i915)
> >                 i915->display.irq.display_irqs_enabled = false;
> >  
> >         intel_hotplug_irq_init(i915);
> > +
> > +       INIT_WORK(&i915->display.irq.vblank_work,
> > intel_display_vblank_work);
> 
> I'd probably also toss in a flush_work() at the end of
> intel_vblank_off() to make sure the work doesn't linger
> past its due date.

Ok, I will add that.

BR,

Jouni Högander
> 



More information about the Intel-gfx mailing list